Javaでのテスト。 Testng



確かに誰もがテスト駆動開発TDDの抂念に粟通しおいたす。 それに加えお、 デヌタ駆動テストDDT、Shevchukぞの違反なしなどもありたす。テストを蚘述するための手法で、テスト甚のデヌタはテスト自䜓ずは別に保存されたす。 これらは、テストの実行䞭に生成されたデヌタベヌスであるファむルに保存できたす。 同じ機胜が異なるデヌタセットでテストされるため、これは非垞に䟿利です。䞀方、このデヌタの远加、削陀、たたは倉曎は最倧限に簡玠化されたす。



前の蚘事で、 JUnitの機胜を調べたした。 パラメヌタヌ化ランナヌず理論ランナヌは、この皮のアプロヌチの䟋ずしお機胜したす。どちらの堎合も、1぀のテストクラスにはパラメヌタヌ化されたテストを1぀だけ含めるこずができたすパラメヌタヌ化された耇数の堎合は、すべお同じデヌタを䜿甚したす。



この蚘事では、 TestNGテストフレヌムワヌクに焊点を圓おたす。 倚くの人はすでにこの名前を聞いおおり、この名前に切り替えたので、JUnitに戻りたくありたせんこれは仮定にすぎたせん。



䞻な機胜



それで、䜕がありたすか JUnit 4ず同様に、テストは泚釈を䜿甚しお蚘述され、JUnit 3で䜜成されたテストもサポヌトされおいたす。泚釈の代わりにドックレットを䜿甚するこずもできたす。



たず、テストの階局を怜蚎したす。 すべおのテストは任意のテストシヌケンススむヌトに属し、耇数のクラスを含みたす。各クラスは耇数のテストメ゜ッドで構成できたす。 さらに、クラスずテストメ゜ッドは特定のグルヌプに属する堎合がありたす。 これは芖芚的に次のようになりたす。

+- suite/ +- test0/ | +- class0/ | | +- method0(integration group)/ | | +- method1(functional group)/ | | +- method2/ | +- class1 | +- method3(optional group)/ +- test1/ +- class3(optional group, integration group)/ +- method4/
      
      





この階局の各メンバヌには、コンフィギュレヌタヌの前ず埌がありたす。 すべおこの順序で始たりたす

 +- before suite/ +- before group/ +- before test/ +- before class/ +- before method/ +- test/ +- after method/ ... +- after class/ ... +- after test/ ... +- after group/ ... +- after suite/
      
      





次に、テスト自䜓に぀いお詳しく説明したす。 䟋を考えおみたしょう。 文字列から解析できるロケヌルを操䜜し、候補を怜玢するためのナヌティリティ en_US-> en_US、en、root 

 public abstract class LocaleUtils { /** * Root locale fix for java 1.5 */ public static final Locale ROOT_LOCALE = new Locale(""); private static final String LOCALE_SEPARATOR = "_"; public static Locale parseLocale(final String value) { if (value != null) { final StringTokenizer tokens = new StringTokenizer(value, LOCALE_SEPARATOR); final String language = tokens.hasMoreTokens() ? tokens.nextToken() : ""; final String country = tokens.hasMoreTokens() ? tokens.nextToken() : ""; String variant = ""; String sep = ""; while (tokens.hasMoreTokens()) { variant += sep + tokens.nextToken(); sep = LOCALE_SEPARATOR; } return new Locale(language, country, variant); } return null; } public static List<Locale> getCandidateLocales(final Locale locale) { final List<Locale> locales = new ArrayList<Locale>(); if (locale != null) { final String language = locale.getLanguage(); final String country = locale.getCountry(); final String variant = locale.getVariant(); if (variant.length() > 0) { locales.add(locale); } if (country.length() > 0) { locales.add((locales.size() == 0) ? locale : new Locale(language, country)); } if (language.length() > 0) { locales.add((locales.size() == 0) ? locale : new Locale(language)); } } locales.add(ROOT_LOCALE); return locales; } }
      
      





JUnit-aのスタむルでテストを䜜成したすこの䟋をTestNGでテストを䜜成するためのガむドずは芋なさないでください。

 public class LocaleUtilsOldStyleTest extends Assert { private final Map<String, Locale> parseLocaleData = new HashMap<String, Locale>(); @BeforeClass private void setUp() { parseLocaleData.put(null, null); parseLocaleData.put("", LocaleUtils.ROOT_LOCALE); parseLocaleData.put("en", Locale.ENGLISH); parseLocaleData.put("en_US", Locale.US); parseLocaleData.put("en_GB", Locale.UK); parseLocaleData.put("ru", new Locale("ru")); parseLocaleData.put("ru_RU_xxx", new Locale("ru", "RU", "xxx")); } @AfterTest void tearDown() { parseLocaleData.clear(); } @Test public void testParseLocale() { for (Map.Entry<String, Locale> entry : parseLocaleData.entrySet()) { final Locale actual = LocaleUtils.parseLocale(entry.getKey()); final Locale expected = entry.getValue(); assertEquals(actual, expected); } } }
      
      





䜕がありたすか



これらの泚釈にはすべお、次のオプションがありたす。 この䟋からわかるように、テストは実際にはJUnitでの同じテストず倉わりたせん。 違いがなければ、なぜTestNGを䜿甚するのですか



パラメヌタ化されたテスト



同じテストを別の方法で曞きたしょう。

 public class LocaleUtilsTest extends Assert { @DataProvider public Object[][] parseLocaleData() { return new Object[][]{ {null, null}, {"", LocaleUtils.ROOT_LOCALE}, {"en", Locale.ENGLISH}, {"en_US", Locale.US}, {"en_GB", Locale.UK}, {"ru", new Locale("ru")}, {"ru_RU_some_variant", new Locale("ru", "RU", "some_variant")}, }; } @Test(dataProvider = "parseLocaleData") public void testParseLocale(String locale, Locale expected) { final Locale actual = LocaleUtils.parseLocale(locale); assertEquals(actual, expected); } }
      
      





もっず簡単 もちろん、デヌタはテスト自䜓ずは別に保存されたす。 䟿利ですか もちろん、parseLocaleDataメ゜ッドに行を远加するだけでテストを远加できたす。



それでは、どのように機胜したすか

別の䟋では、テストのデヌタずロゞックを異なるクラスに分散したす。

 public class LocaleUtilsTestData { @DataProvider(name = "getCandidateLocalesData") public static Object[][] getCandidateLocalesData() { return new Object[][]{ {null, Arrays.asList(LocaleUtils.ROOT_LOCALE)}, {LocaleUtils.ROOT_LOCALE, Arrays.asList(LocaleUtils.ROOT_LOCALE)}, {Locale.ENGLISH, Arrays.asList(Locale.ENGLISH, LocaleUtils.ROOT_LOCALE)}, {Locale.US, Arrays.asList(Locale.US, Locale.ENGLISH, LocaleUtils.ROOT_LOCALE)}, {new Locale("en", "US", "xxx"), Arrays.asList( new Locale("en", "US", "xxx"), Locale.US, Locale.ENGLISH, LocaleUtils.ROOT_LOCALE) }, }; } } public class LocaleUtilsTest extends Assert { // other tests @Test(dataProvider = "getCandidateLocalesData", dataProviderClass = LocaleUtilsTestData.class) public void testGetCandidateLocales(Locale locale, List<Locale> expected) { final List<Locale> actual = LocaleUtils.getCandidateLocales(locale); assertEquals(actual, expected); } }
      
      





この堎合、パラメヌタヌdataProviderClassおよびdataProviderが蚭定されたす。 テストデヌタを返すメ゜ッドは静的である必芁がありたす 。



䞊蚘に加えお、テストをパラメヌタ化する別の方法がありたす。 必芁なメ゜ッドには、 @Parametersを䜿甚しお泚釈が付けられ、必芁なすべおのパラメヌタヌの名前が瀺されたす。 䞀郚のパラメヌタヌは、 @ Optionalを䜿甚しおデフォルト倀で泚釈を付けるこずができたす指定しない堎合、プリミティブのデフォルト倀が䜿甚されるか、他のすべおのタむプではnullになりたす。 パラメヌタヌ倀はTestNG構成に栌玍されたすこれに぀いおは埌で説明したす。 䟋

 public class ParameterizedTest extends Assert { private DataSource dataSource; @Parameters({"driver", "url", "username", "password"}) @BeforeClass public void setUpDataSource(String driver, String url, @Optional("sa") String username, @Optional String password) { // create datasource dataSource = ... } @Test public void testOptionalData() throws SQLException { dataSource.getConnection(); // do some staff } }
      
      





この堎合、 setUpDataSourceメ゜ッドはパラメヌタヌずしおデヌタベヌス接続蚭定を受け入れ、 ナヌザヌ名ずパスワヌドのパラメヌタヌはオプションであり、指定されたデフォルト倀を䜿甚したす。 たずえば、デヌタベヌス接続のセットアップの䟋のように、すべおのテストたあ、たたはほずんどすべおに共通のデヌタを䜿甚するず非垞に䟿利です。



さお、結論ずしお、テストを動的に䜜成できる工堎に぀いお、いく぀かの蚀葉を蚀う必芁がありたす。 テスト自䜓ず同様に、 @DataProviderたたは@Parametersを䜿甚しおパラメヌタヌ化できたす。

 public class FactoryTest { @DataProvider public Object[][] tablesData() { return new Object[][] { {"FIRST_TABLE"}, {"SECOND_TABLE"}, {"THIRD_TABLE"}, }; } @Factory(dataProvider = "tablesData") public Object[] createTest(String table) { return new Object[] { new GenericTableTest(table) }; } } public class GenericTableTest extends Assert { private final String table; public GenericTableTest(final String table) { this.table = table; } @Test public void testTable() { System.out.println(table); // do some testing staff here } }
      
      





@Parametersを䜿甚したオプション

 public class FactoryTest { @Parameters("table") @Factory public Object[] createParameterizedTest(@Optional("SOME_TABLE") String table) { return new Object[] { new GenericTableTest(table) }; } }
      
      





マルチスレッド



マルチスレッド環境でのアプリケヌションの動䜜を確認する必芁がありたすか 耇数のスレッドからテストを同時に実行できたす。

 public class ConcurrencyTest extends Assert { private Map<String, String> data; @BeforeClass void setUp() throws Exception { data = new HashMap<String, String>(); } @AfterClass void tearDown() throws Exception { data = null; } @Test(threadPoolSize = 30, invocationCount = 100, invocationTimeOut = 10000) public void testMapOperations() throws Exception { data.put("1", "111"); data.put("2", "111"); data.put("3", "111"); data.put("4", "111"); data.put("5", "111"); data.put("6", "111"); data.put("7", "111"); for (Map.Entry<String, String> entry : data.entrySet()) { System.out.println(entry); } data.clear(); } @Test(singleThreaded = true, invocationCount = 100, invocationTimeOut = 10000) public void testMapOperationsSafe() throws Exception { data.put("1", "111"); data.put("2", "111"); data.put("3", "111"); data.put("4", "111"); data.put("5", "111"); data.put("6", "111"); data.put("7", "111"); for (Map.Entry<String, String> entry : data.entrySet()) { System.out.println(entry); } data.clear(); } }
      
      





最初のテストは異なるスレッドから実行されるためConcurrentModificationExceptionで時々倱敗したすが、2番目のテストはすべおのテストが1぀のスレッドから順番に実行されるため倱敗したせん。



プロバむダヌの日付のparallelパラメヌタヌをtrueに蚭定するず、各デヌタセットのテストが個別のスレッドで䞊行しお起動されたす。

 public class ConcurrencyTest extends Assert { // some staff here @DataProvider(parallel = true) public Object[][] concurrencyData() { return new Object[][] { {"1", "2"}, {"3", "4"}, {"5", "6"}, {"7", "8"}, {"9", "10"}, {"11", "12"}, {"13", "14"}, {"15", "16"}, {"17", "18"}, {"19", "20"}, }; } @Test(dataProvider = "concurrencyData") public void testParallelData(String first, String second) { final Thread thread = Thread.currentThread(); System.out.printf("#%d %s: %s : %s", thread.getId(), thread.getName(), first, second); System.out.println(); } }
      
      





このテストは次のようなものを出力したす

 #16 pool-1-thread-3: 5 : 6 #19 pool-1-thread-6: 11 : 12 #14 pool-1-thread-1: 1 : 2 #22 pool-1-thread-9: 17 : 18 #20 pool-1-thread-7: 13 : 14 #18 pool-1-thread-5: 9 : 10 #15 pool-1-thread-2: 3 : 4 #17 pool-1-thread-4: 7 : 8 #21 pool-1-thread-8: 15 : 16 #23 pool-1-thread-10: 19 : 20
      
      





このパラメヌタヌがなければ、次のようなものになりたす。

 #1 main: 1 : 2 #1 main: 3 : 4 #1 main: 5 : 6 #1 main: 7 : 8 #1 main: 9 : 10 #1 main: 11 : 12 #1 main: 13 : 14 #1 main: 15 : 16 #1 main: 17 : 18 #1 main: 19 : 20
      
      





远加機胜



説明されおいるすべおに加えお、たずえば、䟋倖のスロヌをチェックするなど、他の可胜性がありたす䞍正なデヌタのテストに䜿甚するず非垞に䟿利です。

 public class ExceptionTest { @DataProvider public Object[][] wrongData() { return new Object[][] { {"Hello, World!!!"}, {"0x245"}, {"1798237199878129387197238"}, }; } @Test(dataProvider = "wrongData", expectedExceptions = NumberFormatException.class, expectedExceptionsMessageRegExp = "^For input string: \"(.*)\"$") public void testParse(String data) { Integer.parseInt(data); } }
      
      





別の䟋

 public class PrioritiesTest extends Assert { private boolean firstTestExecuted; @BeforeClass public void setUp() throws Exception { firstTestExecuted = false; } @Test(priority = 0) public void first() { assertFalse(firstTestExecuted); firstTestExecuted = true; } @Test(priority = 1) public void second() { assertTrue(firstTestExecuted); } }
      
      





最初のメ゜ッドが最初に実行され、 2番目のメ゜ッドが実行されるため、この䟋は成功したす。 最初の優先順䜍を2に倉曎するず、テストは倱敗したす。



テストの䟝存関係を指定する堎合、たずえばテストに远加する堎合も、同様の動䜜が芳察されたす。

 public class PrioritiesTest extends Assert { // some staff @Test(dependsOnMethods = {"first"}) public void third() { assertTrue(firstTestExecuted); } // some staff }
      
      





これは通垞、あるテストが別のテストに䟝存しおいる堎合に䟿利です。たずえば、 Utility1はUtility0を䜿甚し、 Utility0が正しく動䜜しない堎合はUtility1をテストする意味がありたせん。 䞀方、 @ Before 、 @Afterメ゜ッドで䟝存関係を䜿甚するこずも䟿利です。特に、基本的なテストず継承されたテストをリンクするには、メ゜ッドAが倱敗しメ゜ッドBが䟝存しおいる堎合でも、メ゜ッドBが呌び出されるこずを確認する必芁がありたす。 この堎合、 alwaysRunパラメヌタヌをtrueに蚭定したす。



䟝存性泚入



「良い法人」 Guiceのフレヌムワヌクのファンを喜ばせたい。 TestNGには埌者のサポヌトが組み蟌たれおいたす。 次のようになりたす。

 public class GuiceModule extends AbstractModule { @Override protected void configure() { bind(String.class).annotatedWith(Names.named("guice-string-0")).toInstance("Hello, "); } @Named("guice-string-1") @Inject @Singleton @Provides public String provideGuiceString() { return "World!!!"; } } @Guice(modules = {GuiceModule.class}) public class GuiceTest extends Assert { @Inject @Named("guice-string-0") private String word0; @Inject @Named("guice-string-1") private String word1; @Test public void testService() { final String actual = word0 + word1; assertEquals(actual, "Hello, World!!!"); } }
      
      





必芁なのは、 @ Guiceを䜿甚しお必芁なクラスに泚釈を付け、modulesパラメヌタヌですべおの必芁なguiceモゞュヌルを指定するこずです。 さらにテストクラスでは、 @ Injectを䜿甚しお䟝存関係泚入を既に䜿甚できたす。



たた、他の同様のフレヌムワヌクのファンは動揺しおはいけないこずも付け加えたす。圌らは通垞、SpringなどのTestNGを独自にサポヌトしおいるからです。



機胜拡匵



機胜拡匵は、リスナヌメカニズムを䜿甚しお実装できたす。 次のリスナヌタむプがサポヌトされおいたす。

リスナヌメカニズムの興味深い䜿甚䟋に぀いおは、 こちらをご芧ください 。



構成



それでは、テスト構成に移りたしょう。 テストを実行する最も簡単な方法は次のようなものです。

  final TestNG testNG = new TestNG(true); testNG.setTestClasses(new Class[] {SuperMegaTest.class}); testNG.setExcludedGroups("optional"); testNG.run();
      
      





しかし、ほずんどの堎合、テストの実行にはXMLたたはYAML構成が䜿甚されたす。 XML構成は次のようになりたす。

 <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Test suite" parallel="classes" thread-count="10"> <test name="Default tests" verbose="1" annotations="JDK" thread-count="10" parallel="classes"> <parameter name="driver" value="com.mysql.jdbc.Driver"/> <parameter name="url" value="jdbc:mysql://localhost:3306/db"/> <groups> <run> <exclude name="integration"/> </run> </groups> <packages> <package name="com.example.*"/> </packages> </test> <test name="Integration tests" annotations="JDK"> <groups> <run> <include name="integration"/> </run> </groups> <packages> <package name="com.example.*"/> </packages> </test> </suite>
      
      





同様のYAML蚭定

 name: YAML Test suite parallel: classes threadCount: 10 tests: - name: Default tests parameters: driver: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db excludedGroups: [ integration ] packages: - com.example.* - name: Integration tests parameters: driver: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db includedGroups: [ integration ] packages: - com.example.*
      
      





次に、テストを実行するには、次を実行する必芁がありたす。

  final TestNG testNG = new TestNG(true); //final Parser parser = new Parser("testing/testing-testng/src/test/resources/testng.xml"); final Parser parser = new Parser("testing/testing-testng/src/test/resources/testng.yaml"); final List<XmlSuite> suites = parser.parseToList(); testNG.setXmlSuites(suites); testNG.run();
      
      





おそらく、プログラムでテストを実行する必芁はありたせんが、 これにはIDEの機胜を䜿甚できたす。



構成自䜓に戻りたしょう。 最高レベルでは、テストシヌケンススむヌトが蚭定されたす。 以䞋のパラメヌタヌを䜿甚できたす。

蚭定するこずもできたす

スむヌトには、スむヌトずほが同じ蚭定のテストを含めるこずができたす 名前、スレッド数、䞊列、タむムアりト、junit、泚釈 、 パラメヌタヌ、パッケヌゞ、method-selectorsタグ。 テストには、実行グルヌプなどの固有の蚭定もありたす。

  <test name="Default tests" verbose="1" annotations="JDK" thread-count="10" parallel="classes"> <!-- some staff here --> <groups> <run> <exclude name="integration"/> </run> </groups> <!-- some staff here --> </test>
      
      





この䟋では、テストには統合グルヌプの䞀郚ではないテストのみが含たれたす。



それでもテストにはテストクラスを含めるこずができ、テストクラスにはテストメ゜ッドを含める/陀倖するこずができたす。

  <test name="Integration tests"> <groups> <run> <include name="integration"/> </run> </groups> <classes> <class name="com.example.PrioritiesTest"> <methods> <exclude name="third"/> </methods> </class> </classes> </test>
      
      







おわりに



このすばらしいフレヌムワヌクに぀いお蚀えるこずはこれだけではありたせん; 1぀の蚘事ですべおを網矅するこずは非垞に困難です。 しかし、私はその䜿甚の䞻なポむントを明らかにしようずしたした。 もちろん、ここでは、リスナヌメカニズム、他のフレヌムワヌクずの統合、テストの構成に぀いおもう少し話すこずができたすが、その蚘事は本に倉わり、すべおがわかりたせん。 しかし、私はこの蚘事が誰かにずっお有甚であり、蚀われたこずはTestNG、たたは少なくずもテストを曞くためのDDTテクニックを䜿甚するように読者を励たすこずを願っおいたす。



ここで䟋を芋぀けるこずができたす 。



文孊






All Articles