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

Writing parameterized Tests with Spring and JUnit 4

This blog will demonstrate how to write parameterized tests with JUnit4 with Spring.

Let say we have the following interface

public interface Logic {
     boolean xor(boolean a, boolean b);
}

and the corresponding implementation annotated as a Spring service component

@Service
public class LogicImpl implements Logic {
      @Override
      public boolean xor(boolean a, boolean b) {
            return a ^ b;
      }
}

To test the above class with different input combinations of arguments a and b, we can write the following parameterized test in JUnit

@RunWith(Parameterized.class) // Note 1
@SpringApplicationConfiguration(classes = BlogApplication.class)
public class LogicImplTest {

     @Autowired
     private LogicImpl logic;

     // Manually config for spring to use Parameterised
     private TestContextManager testContextManager;

     @Parameter(value = 0) // Note 3i
     public boolean a;

     @Parameter(value = 1) // Note 3ii
     public boolean b;

     @Parameter(value = 2) // Note 3iii
     public boolean expected;

     @Parameters // Note 4
     public static Collection<Object[]> data() {
          Collection<Object[]> params = new ArrayList<>();
          params.add(new Object[] { true, true, false});
          params.add(new Object[] { true, false, true});
          params.add(new Object[] { false, true, true});
          params.add(new Object[] { false, false, false});

          return params;
      }

     @Before // Note 2
     public void setUp() throws Exception {
          this.testContextManager = new TestContextManager(getClass());
          this.testContextManager.prepareTestInstance(this);
     }

     @Test // Note 5
     public void testXor() {
          assertThat(logic.xor(a, b), equalTo(expected));
     }

}

A few things to note here:

  1. The test class is to be run with the Parameterized runner, instead of the normal SpringJUnit4ClassRunner class.
  2. We need to manually configure the test context manager as in the @Before method. This is typically done automatically by Spring
  3. Parameters are defined as public members of the class (as in 3i to 3iii) with the @Parameter annotation. Since we have more than 1 parameter, it is also neccessary to set the value attribute. This defines the index of the parameters to use.
  4. Parameter values are set by implementing a static  method and annotate it with @Parameters. In our example, the data() method returns a list of object arrays. Each value of the list params corresponds to a set of parameter values.
  5.  Tests now can use the parameter values.

Running the test class in Eclipse will give you something like this

blogPTest

Note testXor() is run 4 times, each using the parameter set of the values defined in the list returned by the data() method.

 

Setup Spring RestTemplate to accept Self Signed Cert

This is strictly for testing only but may be useful if you need to perform integration tests. For example, the system you develop needs to access another internal or 3rd party test server via https where the server’s certificate is not signed.

PKIX path building failed

By default, if you try to access a server via https with a self signed certificate, for example with the following codes

RestTemplate template = new TestRestTemplate();
template.getForObject(https://<some server>/, String.class);

you will get the following exception:

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://<some server>":
sun.security.validator.ValidatorException: PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target;

HttpClient

To fix the above, update the RestTemplate with a custom HttpClient that accepts self-signed certificate:

      SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build());

      HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();

      RestTemplate template = new TestRestTemplate();
      ((HttpComponentsClientHttpRequestFactory) template.getRequestFactory()).setHttpClient(httpClient);

and add the following dependency if needed

      <dependency>
           <groupId>org.apache.httpcomponents</groupId>
           <artifactId>httpclient</artifactId>
           <scope>test</scope>
      </dependency>

Again this should only be used for testing purpose only.

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.

Automated Web site testing with Selenium

I will demonstrate in this blog how to set up Selenium to perform automated UI testing of web sites. Selenium provides different “parts” to support different testing purposes/scenarios. In this blog, I will focus on how to set up and run a remote Selenium Server and perform automated testing on a client machine. This setup is probably most appropriate from a development and deployment point of view.

Getting Selenium

First step is to download and set up Selenium Server to run on the server machine:

  1. Download the Selenium Server jar file from here and save it to a folder in your PC, e.g. C:\SeleniumServer
  2. To run tests for a browser other than Firefox using the Selenium server, you also need to download the respective browser driver. E.g. download the Internet Explorer Driver Server and follow the setup instruction for running tests on IE.
  3. To start the server, go to the folder in which jar file is saved and type:

C:\SeleniumServer>java -jar selenium-server-standalone-2.31.0.jar

Set up Project

With the Selenium Server up and running, we can start working on the project to write some UI tests using selenium. First we need to get the selenium jar files for the client. I use Maven and the pom file looks like

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>selenium-test</groupId>
 <artifactId>selenium-test</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <dependencies>
    <dependency>
       <groupId>org.seleniumhq.selenium</groupId>
       <artifactId>selenium-java</artifactId>
       <version>2.31.0</version>
    </dependency>
    <dependency>
       <groupId>com.opera</groupId>
       <artifactId>operadriver</artifactId>
    </dependency>
    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.8.2</version>
    </dependency>
    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
       <version>1.6.4</version>
    </dependency>
 </dependencies>
<dependencyManagement>
 <dependencies>
    <dependency>
       <groupId>com.opera</groupId>
       <artifactId>operadriver</artifactId>
       <version>1.2</version>
       <exclusions>
          <exclusion>
             <groupId>org.seleniumhq.selenium</groupId>
             <artifactId>selenium-remote-driver</artifactId>
          </exclusion>
       </exclusions>
    </dependency>
 </dependencies>
</dependencyManagement>
</project>

Writing first Selenium test

First step here is to create an instance of RemoteWebDriver to connect to the Selenium Server:

@RunWith(Parameterized.class) // [1]
public abstract class AbstractRCTest {
/** Use this object to run all of your selenium tests */
 protected RemoteWebDriver driver; // [2]

 protected String browser;

 static Map<String, DesiredCapabilities> browserCapabilitiesMap; // [3]

 static { // [3]
      browserCapabilitiesMap = new LinkedHashMap<String, DesiredCapabilities>();
      browserCapabilitiesMap.put("*firefox", DesiredCapabilities.firefox());
      browserCapabilitiesMap.put("*iexplore", DesiredCapabilities.internetExplorer());
      browserCapabilitiesMap.put("*googlechrome", DesiredCapabilities.chrome());
 }

 @Parameters
 public static Collection<Object[]> generateData() {
      return Arrays.asList(new Object[][] {
          {"*firefox"},
          {"*googlechrome"}
         });
 }
public AbstractRCTest(String browser) throws MalformedURLException { 
      this.browser = browser;  
      DesiredCapabilities capabilities = browserCapabilitiesMap.get(browser);

      capabilities.setJavascriptEnabled(true);

      URL remoteUrl = new URL("http://localhost:4444/wd/hub"); // [4]
      driver = new RemoteWebDriver(remoteUrl, capabilities); // [5]
 }

Notes:

  1. JUnit4 parameterised test so as to run same tests for different browsers.
  2. This is the instance of RemoteWebDriver to be created to connect to Selenium Server.
  3. This setup the “desired capabilities” to be used as argument to constructor of RemoteWebDriver.
  4. Specify the URL of the Selenium Server. Note the “/wd/hub” at the end.
  5. Instantiate RemoteWebDriver with the URL and desired capabilities.

Now we are ready to write selenium tests. The following example attempts to connect to google.com and check the return page title:

public class SampleRemoteTest extends AbstractRCTest {

 public SampleRemoteTest(String browser) throws MalformedURLException {
      super(browser);
 }
 @Test
 public void testStub() throws Exception {
      driver.get("http://www.google.com");
      assertTrue(driver.getTitle().contains("Google"));
 }
}

The above test should run and pass. One potential gotcha is the Selenium Server requires the respective browser driver to be setup properly or you will get following error:

Exception: The path to the driver executable must be set by the webdriver.<browser>.driver system property...

This means the driver exe has not been put into the system PATH variable nor defined in Selenium Server JVM properties. I find that it is cleaner to do this in the command line when starting up the server, e.g. below is the Windows bat file to startup the Selenium Server for testing Chrome and IE:

java -jar selenium-server-standalone-2.31.0.jar 
-Dwebdriver.ie.driver=%cd%\iedriverserver.exe 
-Dwebdriver.chrome.driver=%cd%\chromedriver.exe

That’s it for now on Selenium.

Unit test Spring RestTemplate webservice client

This blog demonstrates how to setup and test webservice client codes written in Spring RestTemplate.

Obtaining the jar

I am using Spring 3.1 here and have to include the jar file from the spring-mvc-test project into the classpath. The project has since been incorporated into Spring 3.2. See the Spring documentation for details. The codes here are tested against Spring 3.1 and spring-mvc-test. The APIs may be somewhat different in Spring 3.2.

Restful webservice client

My webservice client communicates with the server via XML so I setup RestTemplate with JaxB marshaller and unmarshaller as follows:

    <bean id="exampleRestClient" >
        <property name="template" ref="exampleRestTemplate"></property>
        <property name="baseUrl" value="<server url>"></property>
    </bean>

    <!-- RESTful template and related beans -->
    <bean id="exampleRestTemplate">
        <property name="messageConverters">
            <list>
                   <ref bean="marshallingHttpMessageConverter"/>
            </list>
        </property>
    </bean>

    <bean id="marshallingHttpMessageConverter">
      <property name="marshaller" ref="jaxb2Marshaller" />
      <property name="unmarshaller" ref="jaxb2Marshaller" />
    </bean>

    <bean id="jaxb2Marshaller">
        <property name="contextPath">
                <value>com.blog.resttemplatetest.jaxb</value>
        </property>
        <property name="schema" value="classpath:myschema.xsd"/>
    </bean>

The webservice client implements the normal CRUD functions by delegating to its RestTemplate bean. I will focus on the put function here and demonstrate how to write unit test for it in next section.

@Component(“exampleRestClient”)
public class ExampleRestClient {

private RestTemplate template;
private String baseUrl; // base url

public ExampleResponse add(final ExampleData resource, final ExampleResponse response) {
return put(baseUrl, resource, response);
}

private ExampleResponse put(final String url, final ExampleData resource, final ExampleResponse response) {
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
final HttpEntity<ExampleData> requestEntity = new HttpEntity<ExampleData>(resource, headers);
final ResponseEntity<ExampleResponse> reponseEntity = template.exchange(url, HttpMethod.PUT, requestEntity,
response.getClass());
return reponseEntity.getBody();
}

// getters and setters

Note that instead of using the put() method in RestTemplate, I use its exchange() method in order to get the return response from the web service, as the RestTemplate#put method does not return anything. Both input and output classes ExampleData and ExampleResponse are JAXB objects generated via xjc from the example XML schema myschema.xsd.

Writing unit test for webservice client

spring-mvc-test provides a mock server class MockRestServiceServer to support client testing. For example, to test the put add method in our ExampleRestClient:

...
import static org.springframework.test.web.client.RequestMatchers.method;
import static org.springframework.test.web.client.RequestMatchers.requestTo;
import static org.springframework.test.web.client.ResponseCreators.withSuccess;
...

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:example-test.xml" })
public class StorefrontRestClientTest {
    private MockRestServiceServer mockServer;

    @Autowired
    private ExampleRestClient service;

    @Before
    public void setUp() {
        mockServer = MockRestServiceServer.createServer(service.getTemplate()); // (1)
    }

    private String loadXmlFile(final String filename) throws IOException {
        // load xml file into String ...
    }

    @Test
    public void testAddReturnCorrectResponse() throws Exception {

        final ObjectFactory factory = new ObjectFactory();
        final String responseXml = loadXmlFile("/test/expectedResponse.xml");

        mockServer.expect(requestTo(service.getBaseUrl()))
                  .andExpect(method(HttpMethod.PUT))
                  .andRespond(withSuccess(responseXml, MediaType.APPLICATION_XML)); // (2)

        final ExampleData exampleData= factory.createExampleData();
        final ExampleResponse response = (ExampleResponse) service.add(exampleData, new ExampleResponse());
        assertEquals(200, response.getStatus()); // (3)
        assertEquals(12345, response.getID()); // (3)
        mockServer.verify(); // (4)
    }
    ...
// file: expectedResponse.xml
<?xml version="1.0" encoding="UTF-8"?>
<tns:exampleResponse xmlns:tns="http://" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.example.com/example example.xsd ">
  <tns:status>200</tns:status>
  <tns:ID>12345</tns:ID>
</tns:exampleResponse>

Note:

  1. Instantiate mock server with the RestTemplate to be tested
  2. This sets the expectations on the request and mocks the response to be returned, in this case the Xml string stored in file expectedResponse.xml (above).  Note the static imports required at the top of the codes for requestTo, addExpect and andResponse.
  3. Junit assertions as would normally included.
  4. Call verify() method to ensure that expectations are being met.

Final words

Client side unit testing is useful and important. It allows you to verify that correct requests are generated by the client and responses received from server are handled properly, without the need of a running server. It is handy when the server and client are being developed concurrently.

Testing with ActiveMQ embedded broker

Introduction

This blog demonstrates what you may do to setup and run tests for a JMS message system using ActiveMQ’s embeeded broker. In general, you should focus on testing on your message producers and consumers. However you may also find it useful in some circumstances to write some integration tests to make sure that the various components of the message system are implemented correctly.

Setup ActiveMQ embedded broker

This section shows how to setup a embedded broker in ActiveMQ using an ActiveMQConnectionFactory. First, you need to create a BrokerService instance:

    protected BrokerService createBroker() throws Exception {
        BrokerService service = new BrokerService();
        service.setPersistent(false);
        service.setUseJmx(false);

        // Setup policy
     ...

        connector = service.addConnector(""tcp://localhost:61616");
        return service;
    }

It may be useful to setup the broker policy to match your system setup. For example, to configure broker to wait for consumers before dispatching starts, insert the following codes after “// Setup policy” comment above:

        PolicyMap policyMap = new PolicyMap();
        PolicyEntry policy = new PolicyEntry();
        policy.setConsumersBeforeDispatchStarts(2);
        policy.setTimeBeforeDispatchStarts(1000);
        policyMap.setDefaultEntry(policy);
        service.setDestinationPolicy(policyMap);

Now to create embedded broker using connection factroy:

        ActiveMQConnectionFactory connFactory = 
                  new ActiveMQConnectionFactory(connector.getConnectUri() 
                                           + "?jms.prefetchPolicy.all=1");
        connection = connFactory.createConnection();
        connection.start();

Now we are ready to start the broker. I put the codes altogether into an abstract class for JUnit:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/spring/app-root-test.xml" })
public abstract class AbstractActiveMQJMSTest {

    protected Connection connection;
    protected BrokerService broker;
    protected TransportConnector connector;
    protected int consumersBeforeDispatchStarts;
    protected int timeBeforeDispatchStarts;

    @Value("${jms.broker.url}")
    private String brokerUrl;

    @Before
    public void setUp() throws Exception {
        broker = createBroker();
        broker.start();
        ActiveMQConnectionFactory connFactory = 
              new ActiveMQConnectionFactory(connector.getConnectUri() 
                                       + "?jms.prefetchPolicy.all=1");
        connection = connFactory.createConnection();
        connection.start();
    }

    @After
    public void tearDown() throws Exception {
        connector.stop();
        connection.close();
        broker.stop();
    }

    protected BrokerService createBroker() throws Exception {
       ... // omitted to save space. See codes above
    }

Writing Tests

Writing tests using the above abstract class is straight forward. For example, the codes below demonstrate how to test that all messages from a producer are dispatched to the same consumer (i.e. exclusive consumer). Note this is a make-up test case for the purpose of demonstrating how a unit test can be written and run using the embedded broker in ActiveMQ. In practice you would only need to write unit tests to check message headers are setup properly in the producer to ensure exclusive customer feature is setup correctly.

public class ExclusiveCustomerMessagingTest extends AbstractActiveMQJMSTest {

    @Autowired
    private ISomeProvider provider;

    @Test
    public void testStickyConsumer() throws Exception {
        for (int i = 0; i < 5; i++) {
            provider.send(someMessage);
        }

        // now create some consumers
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(1);

        Destination dest = new ActiveMQQueue("foo-queue");
        MessageConsumerGroupThread worker = 
             new MessageConsumerGroupThread(connection, dest, "foo", 
                                        startSignal, doneSignal, 5);

        MessageConsumerGroupThread worker2 = 
             new MessageConsumerGroupThread(connection, dest, "foo2",
                                         startSignal, doneSignal, 5);

        Thread workerThread = new Thread(worker);
        workerThread.start();
        Thread workerThread2 = new Thread(worker2);
        workerThread2.start();

        startSignal.countDown();
        doneSignal.await(); // this blocks until workers are done

        List<Message> msg = worker.getMessagesReceived();
        List<Message> msg2 = worker2.getMessagesReceived();

        // Test sticky customer
        assertEquals(5, msg.size() + msg2.size());
        assertTrue(msg2.size() == 5 || msg.size() == 5);

        worker.setStop(true);
        worker2.setStop(true);
    }
}

Note MessageConsumerGroupThread class implements Runnable and loops until a prescribed number of messages (5) from the destination have been handled by the internal message consumer or being stopped by the calling test method.

Resources

  1. ActiveMQ documentation on the differnt approaches one can use to create an embedded broker.
  2. ActiveMQ javadoc API.