Trong bài này chúng ta sẽ cùng tìm hiểu về JUnit Listener và cách để lắng nghe các sự kiện mỗi khi một test được thực thi trong JUnit.
Listener là gì?
Listener giúp lắng nghe các event (sự kiện) mà chúng ta quan tâm, chẳng hạn chúng ta thêm listener để ghi log, tracking thời gian mỗi khi một event phát sinh.
JUnit cung cấp một cách để chúng ta lắng nghe các sự kiện trong khi thực thi các phương thức test thông qua class RunListener.
Để tạo một Listener trong JUnit, chúng ta chỉ đơn giản tạo một class Listener kế thừ từ class RunListener và đăng ký (override) các event mà chúng ta muốn lắng nghe.
Một số sự kiện chúng ta có thể đăng ký với RunListener:
- testRunStarted(Description description) : được gọi trước khi bất kỳ test đã được chạy và chỉ được gọi một lần. Description chứa các thông tin về một test được chạy như tên phương thức, số lượng phương thức, …
- testRunFinished(Result result) : được gọi sau khi tất cả các test đã được chạy.
- testStarted(Description description) : được gọi mỗi khi một phương thức test được chạy (@Test).
- testFinished(Description description) : được gọi mỗi khi một phương thức test đã được chạy.
- testFailure(Failure failure) : được gọi mỗi khi một phương thức test failed.
- testAssumptionFailure(Failure failure) : được gọi mỗi khi một assumption failed.
- testIgnored(Description description) : được gọi mỗi khi một phương thức bị bỏ qua (@Ignore).
Ví dụ JUnit Test Listener
Chúng ta có các class test sau:
AssertTest.java
package com.gpcoder.junit.listener; import org.junit.Assert; import org.junit.Test; public class AssertTest { @Test public void assertTrueTest() { Assert.assertTrue(true); } @Test public void assertFalseTest() { Assert.assertFalse(false); } }
IgnoreTest.java
package com.gpcoder.junit.listener; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; public class IgnoreTest { @Test @Ignore("This test case will be ignored") public void ignoreTest() { String expected = "gpcoder.com"; Assert.assertEquals(expected, "gpcoder.com"); } }
Tiếp theo chúng ta sẽ tạo một Listener để lắng nghe các sự kiện khi mỗi một phương thức được thực thi:
package com.gpcoder.junit.listener; import org.junit.Ignore; import org.junit.runner.Description; import org.junit.runner.Result; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; public class ExecutionListener extends RunListener { private long startTime; private long endTime; /** * Called before any tests have been run. */ @Override public void testRunStarted(Description description) throws java.lang.Exception { System.out.println("Tests started! Number of tests to execute : " + description.testCount()); startTime = System.currentTimeMillis(); } /** * Called when all tests have finished */ @Override public void testRunFinished(Result result) throws java.lang.Exception { System.out.println("Tests finished! Number of tests executed: " + result.getRunCount()); endTime = System.currentTimeMillis(); long elapsedMiliSeconds = endTime - startTime; System.out.println("Elapsed time of tests execution: " + elapsedMiliSeconds + " milliseconds"); } /** * Called when an atomic test is about to be started. */ @Override public void testStarted(Description description) throws java.lang.Exception { System.out.println(String.format("Starting execution of test case : %s()", description.getMethodName())); } /** * Called when an atomic test has finished, whether the test succeeds or fails. */ @Override public void testFinished(Description description) throws java.lang.Exception { System.out.println(String.format("Finished execution of test case : %s()", description.getMethodName())); } /** * Called when an atomic test fails. */ @Override public void testFailure(Failure failure) throws java.lang.Exception { System.out.println(String.format("Execution of test case failed : %s('%s')", failure.getDescription().getMethodName(), failure.getMessage())); } /** * Called when a test will not be run, generally because a test method is * annotated with Ignore. */ @Override public void testIgnored(Description description) throws java.lang.Exception { Ignore ignore = description.getAnnotation(Ignore.class); String ignoreMessage = String.format("Execution of test case ignored : %s('%s')", description.getMethodName(), ignore.value()); System.out.println(ignoreMessage); } }
Để đăng ký Listener đã tạo ở trên với JUnit chúng ta có thể thực hiện một trong các cách sau:
- Đăng ký với JUnitCore trong trường hợp chạy JUnit test một cách thủ công:
package com.gpcoder.junit.listener; import org.junit.runner.JUnitCore; public class JUnitListenerExample1 { public static void main(String[] args) { JUnitCore runner = new JUnitCore(); runner.addListener(new ExecutionListener()); runner.run(AssertTest.class, IgnoreTest.class); } }
Kết quả thực thi class Test trên:
Tests started! Number of tests to execute : 4 Starting execution of test case : assertFalseTest() Finished execution of test case : assertFalseTest() Starting execution of test case : assertEqualsTest() Execution of test case failed : assertEqualsTest('This test case is failed') Finished execution of test case : assertEqualsTest() Starting execution of test case : assertTrueTest() Finished execution of test case : assertTrueTest() Execution of test case ignored : ignoreTest('This test case will be ignored') Tests finished! Number of tests executed: 3 Elapsed time of tests execution: 12 milliseconds
- Đăng ký với @RunWith trong trường hợp thực thi các phương thức test trong class Test:
Đầu tiên chúng ta sẽ tạo một class Custom Runner, class này kế thừa từ class BlockJUnit4ClassRunner
package com.gpcoder.junit.listener; import org.junit.AssumptionViolatedException; import org.junit.internal.runners.model.EachTestNotifier; import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.StoppedByUserException; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; public class CustomBlockJUnit4ClassRunner extends BlockJUnit4ClassRunner { public CustomBlockJUnit4ClassRunner(Class<?> klass) throws InitializationError { super(klass); } @Override public void run(RunNotifier notifier) { System.out.println("Executing run()"); // Add Listener. This will register our JUnit Listener. notifier.addListener(new ExecutionListener()); // Get each test notifier EachTestNotifier testNotifier = new EachTestNotifier(notifier, getDescription()); try { // In order capture testRunStarted method // at the very beginning of the test run, we will add below code. // Invoke here the run testRunStarted() method notifier.fireTestRunStarted(getDescription()); Statement statement = classBlock(notifier); statement.evaluate(); } catch (AssumptionViolatedException e) { testNotifier.fireTestIgnored(); } catch (StoppedByUserException e) { throw e; } catch (Throwable e) { testNotifier.addFailure(e); } } }
Bây giờ chúng ta có thể sử dụng với @RunWith như sau:
package com.gpcoder.junit.listener; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(CustomBlockJUnit4ClassRunner.class) public class JUnitListenerExample2 { @Test public void assertTrueTest() { Assert.assertTrue(true); } @Test public void assertFalseTest() { Assert.assertFalse(false); } @Test @Ignore("This test case will be ignored") public void ignoreTest() { String expected = "gpcoder.com"; Assert.assertEquals(expected, "gpcoder.com"); } }
- Đăng ký với @RunWith trong trường hợp thực thi các phương thức test trong class Suite:
Đầu tiên chúng ta sẽ tạo một class Custom Runner, class này kế thừa từ class Suite
package com.gpcoder.junit.listener; import java.util.List; import org.junit.AssumptionViolatedException; import org.junit.internal.runners.model.EachTestNotifier; import org.junit.runner.Runner; import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.StoppedByUserException; import org.junit.runners.Suite; import org.junit.runners.model.InitializationError; import org.junit.runners.model.RunnerBuilder; import org.junit.runners.model.Statement; public class CustomSuite extends Suite { public CustomSuite(Class<?> klass, List<Runner> runners) throws InitializationError { super(klass, runners); } public CustomSuite(Class<?> klass, RunnerBuilder builder) throws InitializationError { super(klass, builder); } public CustomSuite(RunnerBuilder builder, Class<?> klass, Class<?>[] suiteClasses) throws InitializationError { super(builder, klass, suiteClasses); } public CustomSuite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError { super(builder, classes); } protected CustomSuite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError { super(klass, suiteClasses); } @Override public void run(RunNotifier notifier) { System.out.println("Executing run()"); // Add Listener. This will register our JUnit Listener. notifier.addListener(new ExecutionListener()); // Get each test notifier EachTestNotifier testNotifier = new EachTestNotifier(notifier, getDescription()); try { // In order capture testRunStarted method // at the very beginning of the test run, we will add below code. // Invoke here the run testRunStarted() method notifier.fireTestRunStarted(getDescription()); Statement statement = classBlock(notifier); statement.evaluate(); } catch (AssumptionViolatedException e) { testNotifier.fireTestIgnored(); } catch (StoppedByUserException e) { throw e; } catch (Throwable e) { testNotifier.addFailure(e); } } }
Bây giờ chúng ta có thể sử dụng với @RunWith như sau:
package com.gpcoder.junit.listener; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(CustomSuite.class) @Suite.SuiteClasses({ AssertTest.class, IgnoreTest.class }) public class JUnitListenerExample3 { }
Tài liệu tham khảo: