Panduan untuk Peraturan JUnit 4

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat ciri Peraturan yang disediakan oleh perpustakaan JUnit 4.

Kita akan mulakan dengan memperkenalkan Model Peraturan JUnit sebelum membaca peraturan asas terpenting yang disediakan oleh pengedaran. Selain itu, kita juga akan melihat cara menulis dan menggunakan Peraturan JUnit tersuai kita sendiri.

Untuk mengetahui lebih lanjut mengenai ujian dengan JUnit, lihat siri JUnit komprehensif kami.

Perhatikan bahawa jika anda menggunakan JUnit 5, peraturan telah diganti dengan model Extension.

2. Pengenalan Peraturan JUnit 4

Peraturan JUnit 4 menyediakan mekanisme fleksibel untuk meningkatkan ujian dengan menjalankan beberapa kod di sekitar pelaksanaan kes ujian . Dari segi tertentu, ia serupa dengan mempunyai @Sebelum dan @Setelah anotasi di kelas ujian kami.

Bayangkan kita mahu menyambung ke sumber luaran seperti pangkalan data semasa persediaan ujian dan kemudian tutup sambungan setelah ujian kita selesai. Sekiranya kita ingin menggunakan pangkalan data itu dalam beberapa ujian, kita akan menggandakan kod tersebut dalam setiap ujian.

Dengan menggunakan peraturan, kita dapat mengasingkan semua perkara di satu tempat dan menggunakan semula kod dengan mudah dari pelbagai kelas ujian.

3. Menggunakan Peraturan JUnit 4

Jadi bagaimana kita boleh menggunakan peraturan? Kita boleh menggunakan peraturan JUnit 4 dengan mengikuti langkah mudah berikut:

  • Tambahkan medan awam ke kelas ujian kami dan pastikan bahawa jenis medan ini adalah subjenis antara muka org.junit.rules.TestRule
  • Nyatakan medan dengan anotasi @ Peraturan

Di bahagian seterusnya, kita akan melihat kebergantungan projek apa yang perlu kita mulakan.

4. Pergantungan Maven

Pertama, mari tambah pergantungan projek yang kita perlukan untuk contoh kita. Kami hanya memerlukan perpustakaan JUnit 4 utama:

 junit junit 4.12  

Seperti biasa, kami boleh mendapatkan versi terbaru dari Maven Central.

5. Peraturan yang diperuntukkan dalam Pembahagian

Sudah tentu, JUnit menyediakan beberapa peraturan yang berguna dan ditentukan sebelumnya sebagai sebahagian daripada perpustakaan . Kita boleh mendapatkan semua peraturan ini dalam pakej org.junit.rules .

Di bahagian ini, kita akan melihat beberapa contoh cara menggunakannya.

5.1. The TemporaryFolder Peraturan

Semasa menguji, kita sering memerlukan akses ke fail atau folder sementara. Walau bagaimanapun, mengurus pembuatan dan penghapusan fail-fail ini boleh membebankan. Dengan menggunakan aturan TemporaryFolder , kita dapat mengatur pembuatan file dan folder yang harus dihapus ketika metode pengujian berakhir :

@Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); @Test public void givenTempFolderRule_whenNewFile_thenFileIsCreated() throws IOException { File testFile = tmpFolder.newFile("test-file.txt"); assertTrue("The file should have been created: ", testFile.isFile()); assertEquals("Temp folder and test file should match: ", tmpFolder.getRoot(), testFile.getParentFile()); }

Seperti yang kita lihat, pertama kita menentukan peraturan tmpFolder TemporaryFolder . Seterusnya, kaedah ujian kami membuat fail yang dipanggil test-file.txt dalam folder sementara. Kami kemudian memeriksa bahawa fail telah dibuat dan ada di tempat yang sepatutnya. Sungguh bagus dan sederhana!

Apabila ujian selesai, folder dan fail sementara harus dihapus. Walau bagaimanapun, peraturan ini tidak memeriksa sama ada penghapusan berjaya atau tidak.

Terdapat juga beberapa kaedah menarik lain yang perlu disebutkan di kelas ini:

  • newFile()

    Sekiranya kami tidak memberikan nama fail, maka kaedah ini membuat fail baru yang diberi nama secara rawak.

  • newFolder(String... folderNames)

    Untuk membuat folder sementara yang mendalam secara rekursif, kita boleh menggunakan kaedah ini.

  • newFolder()

    Begitu juga, kaedah newFolder () membuat folder baru bernama secara rawak.

Tambahan yang baik yang perlu disebutkan ialah bermula dengan versi 4.13, peraturan TemporaryFolder membenarkan pengesahan sumber yang dihapuskan:

@Rule public TemporaryFolder folder = TemporaryFolder.builder().assureDeletion().build();

Sekiranya sumber tidak dapat dihapuskan, ujian dengan gagal dengan AssertionError .

Akhirnya, di JUnit 5, kita dapat mencapai fungsi yang sama menggunakan sambungan Direktori Sementara.

5.2. The ExpectedException Peraturan

Seperti namanya, kita dapat menggunakan aturan ExpectedException untuk mengesahkan bahawa beberapa kod melemparkan pengecualian yang diharapkan:

@Rule public final ExpectedException thrown = ExpectedException.none(); @Test public void givenIllegalArgument_whenExceptionThrown_MessageAndCauseMatches() { thrown.expect(IllegalArgumentException.class); thrown.expectCause(isA(NullPointerException.class)); thrown.expectMessage("This is illegal"); throw new IllegalArgumentException("This is illegal", new NullPointerException()); }

Seperti yang dapat kita lihat dalam contoh di atas, pertama-tama kita menyatakan peraturan ExpectedException . Kemudian, dalam ujian kami, kami menegaskan bahawa IllegalArgumentException dilemparkan.

Dengan menggunakan peraturan ini, kami juga dapat mengesahkan beberapa sifat pengecualian lain, seperti pesan dan sebabnya.

Untuk panduan mendalam untuk mengecualikan pengecualian dengan JUnit, lihat panduan terbaik kami mengenai cara Menetapkan Pengecualian.

5.3. The TestName Peraturan

Secara ringkasnya, yang TestName memerintah menyediakan nama ujian semasa dalam kaedah ujian yang diberikan:

@Rule public TestName name = new TestName(); @Test public void givenAddition_whenPrintingTestName_thenTestNameIsDisplayed() { LOG.info("Executing: {}", name.getMethodName()); assertEquals("givenAddition_whenPrintingTestName_thenTestNameIsDisplayed", name.getMethodName()); }

Dalam contoh sepele ini, ketika kita menjalankan ujian unit, kita harus melihat nama ujian dalam output:

INFO c.baeldung.rules.JUnitRulesUnitTest - Executing: givenAddition_whenPrintingTestName_thenTestNameIsDisplayed

5.4. The Timeout Peraturan

Dalam contoh seterusnya ini, kita akan melihat peraturan Timeout . Peraturan ini menawarkan alternatif yang berguna untuk menggunakan parameter timeout pada anotasi Uji individu .

Sekarang, mari kita lihat cara menggunakan peraturan ini untuk menetapkan waktu tamat global pada semua kaedah ujian di kelas ujian kami:

@Rule public Timeout globalTimeout = Timeout.seconds(10); @Test public void givenLongRunningTest_whenTimout_thenTestFails() throws InterruptedException { TimeUnit.SECONDS.sleep(20); }

Dalam contoh sepele di atas, pertama-tama kita menentukan batas waktu global untuk semua kaedah ujian selama 10 saat . Kemudian kami sengaja menentukan ujian yang akan memakan masa lebih dari 10 saat.

Semasa menjalankan ujian ini, kita akan melihat kegagalan ujian:

org.junit.runners.model.TestTimedOutException: test timed out after 10 seconds ...

5.5. The ErrorCollector Rule

Next up we're going to take a look at the ErrorCollector rule. This rule allows the execution of a test to continue after the first problem is found.

Let's see how we can use this rule to collect all the errors and report them all at once when the test terminates:

@Rule public final ErrorCollector errorCollector = new ErrorCollector(); @Test public void givenMultipleErrors_whenTestRuns_thenCollectorReportsErrors() { errorCollector.addError(new Throwable("First thing went wrong!")); errorCollector.addError(new Throwable("Another thing went wrong!")); errorCollector.checkThat("Hello World", not(containsString("ERROR!"))); }

In the above example, we add two errors to the collector. When we run the test, the execution continues, but the test will fail at the end.

In the output, we will see both errors reported:

java.lang.Throwable: First thing went wrong! ... java.lang.Throwable: Another thing went wrong!

5.6. The Verifier Rule

The Verifier rule is an abstract base class that we can use when we wish to verify some additional behavior from our tests. In fact, the ErrorCollector rule we saw in the last section extends this class.

Let's now take a look at a trivial example of defining our own verifier:

private List messageLog = new ArrayList(); @Rule public Verifier verifier = new Verifier() { @Override public void verify() { assertFalse("Message Log is not Empty!", messageLog.isEmpty()); } }; 

Here, we define a new Verifier and override the verify() method to add some extra verification logic. In this straightforward example, we simply check to see that the message log in our example isn't empty.

Now, when we run the unit test and add a message, we should see that our verifier has been applied:

@Test public void givenNewMessage_whenVerified_thenMessageLogNotEmpty() { // ... messageLog.add("There is a new message!"); }

5.7. The DisableOnDebug Rule

Sometimes we may want to disable a rule when we're debugging. For example, it’s often desirable to disable a Timeout rule when debugging to avoid our test timing out and failing before we've had time to debug it properly.

The DisableOnDebug Rule does precisely this and allows us to label certain rules to be disabled when debugging:

@Rule public DisableOnDebug disableTimeout = new DisableOnDebug(Timeout.seconds(30));

In the example above we can see that in order to use this rule, we simply pass the rule we want to disable to the constructor.

The main benefit of this rule is that we can disable rules without making any modifications to our test classes during debugging.

5.8. The ExternalResource Rule

Typically, when writing integration tests, we may wish to set up an external resource before a test and tear it down afterward. Thankfully, JUnit provides another handy base class for this.

We can extend the abstract class ExternalResource to set up an external resource before a test, such as a file or a database connection. In fact, the TemporaryFolder rule we saw earlier extends ExternalResource.

Let's take a quick look at how we could extend this class:

@Rule public final ExternalResource externalResource = new ExternalResource() { @Override protected void before() throws Throwable { // code to set up a specific external resource. }; @Override protected void after() { // code to tear down the external resource }; };

In this example, when we define an external resource we simply need to override the before() method and after() method in order to set up and tear down our external resource.

6. Applying Class Rules

Up until now, all the examples we've looked at have applied to single test case methods. However, sometimes we might want to apply a rule at the test class level. We can accomplish this by using the @ClassRule annotation.

This annotation works very similarly to @Rule but wraps a rule around a whole test — the main difference being that the field we use for our class rule must be static:

@ClassRule public static TemporaryFolder globalFolder = new TemporaryFolder();

7. Defining a Custom JUnit Rule

As we've seen, JUnit 4 provides a number of useful rules out of the box. Of course, we can define our own custom rules. To write a custom rule, we need to implement the TestRule interface.

Let's take a look at an example of defining a custom test method name logger rule:

public class TestMethodNameLogger implements TestRule { private static final Logger LOG = LoggerFactory.getLogger(TestMethodNameLogger.class); @Override public Statement apply(Statement base, Description description) { logInfo("Before test", description); try { return new Statement() { @Override public void evaluate() throws Throwable { base.evaluate(); } }; } finally { logInfo("After test", description); } } private void logInfo(String msg, Description description) { LOG.info(msg + description.getMethodName()); } }

As we can see, the TestRule interface contains one method called apply(Statement, Description) that we must override to return an instance of Statement. The statement represents our tests within the JUnit runtime. When we call the evaluate() method, this executes our test.

In this example, we log a before and after message and include from the Description object the method name of the individual test.

8. Using Rule Chains

In this final section, we'll take a look at how we can order several test rules using the RuleChain rule:

@Rule public RuleChain chain = RuleChain.outerRule(new MessageLogger("First rule")) .around(new MessageLogger("Second rule")) .around(new MessageLogger("Third rule"));

Dalam contoh di atas, kami membuat rangkaian tiga peraturan yang hanya mencetak mesej yang dihantar ke setiap pembina MessageLogger .

Semasa menjalankan ujian, kami akan melihat bagaimana rantai ini diterapkan mengikut urutan:

Starting: First rule Starting: Second rule Starting: Third rule Finished: Third rule Finished: Second rule Finished: First rule

9. Kesimpulannya

Sebagai kesimpulan, dalam tutorial ini, kami telah menerangkan peraturan JUnit 4 secara terperinci.

Pertama, kami memulakan dengan menerangkan peraturan apa dan bagaimana kami dapat menggunakannya. Seterusnya, kami melihat peraturan yang mendalam sebagai sebahagian daripada pengedaran JUnit.

Akhirnya, kami melihat bagaimana kami dapat menentukan peraturan tersuai kami sendiri dan bagaimana menyatukan peraturan bersama.

Seperti biasa, kod sumber penuh artikel terdapat di GitHub.