Using catch-exception in JUnit Tests

I use ExpectedException a lot in my JUnit test to verify exceptions thrown. In some circumstances, you would also want to run some assertions in the tests after the exception is thrown. This is impossible with ExpectedException. In this post, I will demonstrate how to use catch-exception instead to achieve this.

Set up

Let say we have the following class we want to test:

public class MyService {

 private MyDao dao;
 
 public MyService(MyDao dao) {
      this.dao = dao;
 }
 
 public void someMethod() {
      dao.beforeException();
      // Some codes here. Throw an exception for demo purpose
      if (true) {
           throw new NullPointerException("NPE!");
      }
      dao.afterException();
 }
 
}

Note the method someMethod() will throw an NullPointerException. This simulates real codes that throws an exception when the object in certain state or when one of the methods it calls within returns with an exception.

ExpectedException

ExpectedException works by wrapping the entire test method by its own try-catch block. As a result, any assertion statements after the method that throws the exception are not called.

public class MyTest {

 @Rule
 public ExpectedException none = ExpectedException.none();
 
 @Test
 public void testUsingExpectedException() {
      MyDao mockDao = mock(MyDao.class);
      MyService service = new MyService(mockDao);
      none.expect(NullPointerException.class);
      service.someMethod();
 
      // Note the following line is not called. Test still passes
      verify(mockDao).afterException();
 }

Note the test above verifies that the NPE is thrown as expected. The dao’s afterException method is never called and the test will still pass.

Catch-Exception

Catch-exception is a library that would address the issue above with ExpectedException. To use it, include the following Maven dependency

 <dependency>
      <groupId>eu.codearte.catch-exception</groupId>
      <artifactId>catch-exception</artifactId>
      <version>1.4.4</version>
      <scope>test</scope> <!-- test scope to use it only in tests -->
 </dependency>

and update the test as follows

 @Test
 public void testUsingCatchException() {
      MyDao mockDao = mock(MyDao.class);
      MyService service = new MyService(mockDao);
 
      catchException(service).someMethod();
 
      // Hamcrest
      assertThat(caughtException(), instanceOf(NullPointerException.class));
 
      // Mockito verify got called
      verify(mockDao).beforeException();
      verify(mockDao, never()).afterException();
 }

Now the assertion (mockito verify) statements are called. This could be useful if you want to verify that the object is in the correct state or as in the example certain methods of the object’s member is (not) called when the method throws an exception.

Advertisements

Capture screenshots when Selenium tests fail

This blog continues on my last blog on Selenium and demonstrates how to setup Junit4 to capture screenshots of the browser when  tests fail.

Create JUnit Rule

First, the rule in which JUnit uses to run and report test methods needs to be updated. This is done by implementing the interface org.junit.rules.MethodRule. JUnit provides a base class TestWatchman where we can extend:

public class ScreenShotRule extends TestWatchman {

...
 @Override
 public void failed(Throwable e, FrameworkMethod method) {
 // capture screenshot here
 }
}

Capture screenshots with Selenium

Selenium WebDriver implements the interface TakesScreenshot which indicates the web driver has the capability to capture screenshots. To enable this, the RemoteWebDriver instance needs to be augmented as below.

// In class AbstractRCTest (see my previous blog)
driver = new RemoteWebDriver(remoteUrl, capabilities);

 // Augment web driver for screenshot capture
 WebDriver augmentedDriver = new Augmenter().augment(driver);
 screenShotRule = new ScreenShotRule(augmentedDriver);

Now we can implement screenshot capture in class ScreenShotRule to take screenshots with Selenium and save it to file:

@Override
 public void failed(Throwable e, FrameworkMethod method) {
      File scrFile = ((TakesScreenshot) webdriver).getScreenshotAs(
                      OutputType.FILE);
      String scrFilename = method.getName() + "-Screenshot.png";
      File outputFile = new File("C:\\Temp", scrFilename);
      try {
           FileUtils.copyFile(scrFile, outputFile);
      } catch (IOException ioe) {
           log.error("Error copying screenshot after exception.", ioe);
      }
 }

Add rule to JUnit test class

Last thing to do is to set new rule in the unit test class

public class SampleRemoteTest {
     @Rule
     public ScreenShotRule screenShotRule;
     ...
}

That’s it. Now if a test fails, a screenshot will be taken of the browser in the server machine and saved into a local client where the test is run.