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.

 

Advertisements

Using Spring 4 Condition To Control Bean Registration

Spring 4 introduces the Condition interface which supports a condition to checked and registers a bean only when the condition is matched. In this blog, I will demonstrate how to implement a custom condition interface which will register beans when the active profiles match a regex defined in the condition. This would be useful, for example, to inject mock beans when testing while using the actual implementations at deployment. The classes implemented here is a slight modification of the standard @Profile annotation and supporting classes.

@ProfileRegex

First we define the interface @ProfileRegex as follows

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Conditional(ProfileRegexCondition.class)
public @interface ProfileRegex {
      /**
      * The profile regex for which the annotated component should be registered.
      */
      String value();
}

The interface requires a String value attribute which define the regex to use to match the spring context’s active profiles. Note the use of @Condition annotation to define the class that implements the Condition interface, i.e. ProfileRegexCondition.

ProfileRegexCondition

The class ProfileRegexCondition implements the Condition interface

class ProfileRegexCondition implements Condition {

 @Override
 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
       Environment environment = context.getEnvironment();
       if (environment != null) {
            String[] profiles = environment.getActiveProfiles();
            MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(ProfileRegex.class.getName());
           if (attrs != null) {
                for (Object value : attrs.get("value")) {
                     for (String profile : profiles) {
                          boolean matches = Pattern.matches((String) value, profile);
                          if (matches) {
                             return true;
                          }
                     }
                }
                return false;
          }
      }
      return true;
 }
}

The Condition interface has only one method matches() which determines whether the condition is matched. In our implementation, it iterates all the active profiles and return true if the profile matches the regex as defined in the value of the @ProfileRegex annotation.

Example

Suppose we want to register a mock bean for unit testing. We could define the following bean configuration class

@Configuration
@EnableAutoConfiguration
@ComponentScan
@ProfileRegex("test")
public class TestConfiguration {
     @Bean
     MyService myServiceMockBean() {
         return mock(MyService.class);
     }
}

Note the use of @ProfileRegex annotation to only register the beans when the active profile matches the regex “test”.  Also, the @ProfileRegex annotation can also be applied to the individual @Bean method to provide a more fine control over bean registration. The following configuration class does the opposite of the above and registers whenever the active profiles does not match the regex “test”.

@Configuration
@ProfileRegex("((?!test).)*")
public class ServiceConfiguration {

    @Bean
    MyService myServiceBean() {
         return new MyServiceImpl();
    }
}

Below is the unit test to verify the correct (mock) bean is registered

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@ActiveProfiles({"test"})
public class ServiceConfigTest {
 
    @Autowired
    private ApplicationContext context;

    @Test
    public void testProfileRegexCondition() {
         MyService service = context.getBean(MyService.class);
         assertThat(service, CoreMatchers.notNullValue());
         assertThat(new MockUtil().isMock(service), equalTo(true));
    }
}

Finally, Spring Boot provides a number of useful conditional annotations under the package org.springframework.boot.autoconfigure.condition. See the javadoc for more details.

Setup Spring Security with Active Directory LDAP in Spring Boot Web Application

This post illustrates how to set up Spring Security in Spring Boot configuration with Active Directory LDAP for a Spring MVC web application. I will also show what needs to be configured for the embedded tomcat to accept HTTPS.

Spring Security with LDAP

To configure Spring Security in Spring Boot, add the following Configuration class to your project. Note the use of annotation @EnableWebMvcSecurity. The configuration class extends the WebSecurityConfigurerAdapter class in Spring Security. More information can be found in the Spring Security Reference here.

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

     @Value("${ldap.domain}")
     private String DOMAIN;

     @Value("${ldap.url}")
     private String URL;

     @Value("${http.port}")
     private int httpPort;

     @Value("${https.port}")
     private int httpsPort;

     @Override
     protected void configure(HttpSecurity http) throws Exception {
          /*
           * Set up your spring security config here. For example...
          */
          http.authorizeRequests().anyRequest().authenticated().and().formLogin().loginUrl("/login").permitAll();
          /*
           * Use HTTPs for ALL requests
          */
          http.requiresChannel().anyRequest().requiresSecure();
          http.portMapper().http(httpPort).mapsTo(httpsPort);
     }

     @Override
     protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
          authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
     }

     @Bean
     public AuthenticationManager authenticationManager() {
          return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
     }
     @Bean
     public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
          ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(DOMAIN, URL);
          provider.setConvertSubErrorCodesToExceptions(true);
          provider.setUseAuthenticationRequestCredentials(true);
          return provider;
     }
}

Add HTTPS connector for embedded Tomcat in Spring Boot

Now that Spring Security is set up, you need to update the web server to accept requests from HTTPS. To do that using the embedded Tomcat server in Spring Boot, add the following EmbeddedServletContainerCustomizer bean to the application configuration as shown below. Note I am using anonymous inner classes here instead of lambda expression as I see in other examples for Java 7 compatibility. You will need a keystore file for this to work.

@Bean
EmbeddedServletContainerCustomizer containerCustomizer (

     @Value("${https.port}") final int port, 
     @Value("${keystore.file}") Resource keystoreFile,
     @Value("${keystore.alias}") final String alias, 
     @Value("${keystore.password}") final String keystorePass,
     @Value("${keystore.type}") final String keystoreType) throws Exception {
          final String absoluteKeystoreFile = keystoreFile.getFile().getAbsolutePath();
          return new EmbeddedServletContainerCustomizer() {
               public void customize(ConfigurableEmbeddedServletContainer container) {
                    TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container;
                    tomcat.addConnectorCustomizers(new TomcatConnectorCustomizer() {
                         public void customize(Connector connector) {
                              connector.setPort(port);
                              connector.setSecure(true);
                              connector.setScheme("https");
                              Http11NioProtocol proto = (Http11NioProtocol) connector.getProtocolHandler();
                              proto.setSSLEnabled(true);
                              proto.setKeystoreFile(absoluteKeystoreFile);
                              proto.setKeyAlias(alias);
                              proto.setKeystorePass(keystorePass);
                              proto.setKeystoreType(keystoreType);
                        }
               });
           }
     };
 }

 

Including Field Value In Validation Message Using Spring Validation framework for JSR-303

This blog post demonstrates how to include the value of the field in the error message when using Spring Validation framework support for JSR-303. This has been made possible in Bean Validation API 1.1 with the new support of error message interpolation using EL expression. In particular, the use of the parameter “validatedValue” in the message. This post is inspired by the blog “Better Error Messages With Bean Validation 1.1 In Spring MVC Application” and will focus on the Spring Validation framework alone and demonstrate alternative ways to setup message keys using validateValue

Application Setup

I am using Spring 4 and Hibernate Validator 5.1.1 in the example here.

Spring Configuration

Nothing special with the spring configuration file (app-config.xml) here. The messageAccessor bean is used by the test class to get the validation error messages.

<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns=”http://www.springframework.org/schema/beans&#8221;
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns:mvc=”http://www.springframework.org/schema/mvc&#8221;
xmlns:context=”http://www.springframework.org/schema/context&#8221;
xsi:schemaLocation=”
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd”&gt;

<!– Creates the JSR-303 Validator –>
<bean id=”validator” class=”org.springframework.validation.beanvalidation.LocalValidatorFactoryBean” >
<property name=”validationMessageSource” ref=”messageSource”/>
</bean>

<bean id=”messageSource” class=”org.springframework.context.support.ResourceBundleMessageSource”>
<property name=”basenames”>
<list>
<value>messages</value>
</list>
</property>
</bean>

<bean id=”messageAccessor” class=”org.springframework.context.support.MessageSourceAccessor”>
<constructor-arg index=”0″ ref=”messageSource”/>
</bean>

</beans>

Order Class

Let say we have the following class we need to validate against. For simplicity, all the 3 fields have to be of at least 10 characters. The javax validation annotation @Size(min = 10) is hence used.


public class Order {

@Size(min = 10, message = “{Size.order.customerName.custom} ${validatedValue}”)
private String customerName;

@Size(min = 10, message =”phone number entered [${validatedValue}] is invalid. It must have at least {min} digits”)
private String phoneNumber;

@Size(min = 10)
private String address;

// getter and setter omitted here …

messages.properties file

The error messages are defined in the messages.properties file (shown below) and are used by the validator to resolve the message keys against.

javax.validation.constraints.Size.message=Invalid size for input: ${validatedValue}
Size.order.customerName.custom=Invalid customer name:

Test Class

Now we are ready to write some tests

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“/spring/app-config.xml”)
public class OrderValidationTest {

@Autowired
private Validator validator;

@Autowired
private MessageSourceAccessor messageSourceAccessor;

@Test
public void testValidateOrder() {

Order order = new Order();
order.setCustomerName(“a”);
order.setPhoneNumber(“1234”);
order.setAddress(“b”);
BindingResult errors = new BeanPropertyBindingResult(order, “order”);
validator.validate(order, errors);
assertEquals(“Invalid customer name: a”, getValidationErrorMessage(errors, “customerName”));
assertEquals(“phone number entered [1234] is invalid. It must have at least 10 digits”, getValidationErrorMessage(errors, “phoneNumber”));
assertEquals(“Invalid size for input: b”, getValidationErrorMessage(errors, “address”));

}

private String getValidationErrorMessage(BindingResult result, String field) {

if (result.hasErrors()) {
FieldError fieldError = result.getFieldError(field);
return messageSourceAccessor.getMessage(fieldError);
}
return “”;

}

}

The example classes above demonstrate 3 different ways to include the value of the invalid field in the error message, all using the validatedValue parameter supported by Bean Validation 1.1 API.

1. Update default message for the validator

First option is to modify the default message key used by the validation annotation as shown in the field address in the messages.properties file as shown below:

@Size(min = 10)
private String address;

// messages.properties

javax.validation.constraints.Size.message=Invalid size for input: ${validatedValue}

2.  Add message attribute to the validation annotation

To include validatedValue only for certain field in a class, add message attribute to the validator annotation:

@Size(min = 10, message =”phone number entered [${validatedValue}] is invalid. It must have at least {min} digits”)
private String phoneNumber;

3. Add custom message key in the message attribute

It is possible to use message key by enclosing with {} if you do not want to hard coded message text in the class:

@Size(min = 10, message = “{Size.order.customerName.custom} ${validatedValue}”)
private String customerName;

// messages.properties

Size.order.customerName.custom=Invalid customer name:

You cannot use the same key as expected by Spring, i.e. Size.order.customerName. Note also you cannot just add “${validatedValue}” in the text in messages.properties file.

That’s it. Hope this post helps.

Resources

  1. Hibernate Validator 5.1. documentation – See this chapter for more details on message interpolation.

Set up a full broker for Spring 4 STOMP over WebSocket messaging using ActiveMQ

Spring 4 websocket comes with a built-in “simple” broker for handling messaging in memory. In this blog, I will demonstrate how to configure Spring 4 to use a “full” broker, i.e. ActiveMQ, to support STOMP over WebSocket messaging.

1. Enable ActiveMQ for STOMP

First, we need to enable STOMP protocol support in ActiveMQ. This can be done by adding the connector in the activemq.xml file as shown below.

 <transportConnectors>
       <transportConnector name="openwire" uri="tcp://0.0.0.0:61616"/> 
       <transportConnector uri="stomp://localhost:61613"/>
 </transportConnectors>

You may also setup security as mentioned in the ActiveMQ documentation here.

2. Add reactor-tcp jar files

Add to following dependencies to the pom.xml file:

 <!-- Reactor for websocket relay to MQ-->
 <dependency>
      <groupId>org.projectreactor</groupId>
      <artifactId>reactor-core</artifactId>
      <version>1.0.0.RELEASE</version>
 </dependency>
 <dependency>
      <groupId>org.projectreactor</groupId>
      <artifactId>reactor-tcp</artifactId>
      <version>1.0.0.RELEASE</version>
 </dependency>

Or download the jar files reactor-core-1.0.0.RELEASE.jar and reactor-tcp-1.0.0.RELEASE.jar into your classpath

3. Update Spring WebSocket config

Replace the simple broker config below

 <websocket:message-broker application-destination-prefix="/app">
       <websocket:stomp-endpoint path="/hello">
             <websocket:sockjs/>
       </websocket:stomp-endpoint>
       <websocket:simple-broker prefix="/topic,/queue"/>
 </websocket:message-broker>

with a full broker:

 <websocket:message-broker application-destination-prefix="/app">
       <websocket:stomp-endpoint path="/hello">
            <websocket:sockjs/>
       </websocket:stomp-endpoint>
       <websocket:stomp-broker-relay prefix="/topic,/queue"
           relay-host="localhost" relay-port="61613"
           heartbeat-send-interval="20000" heartbeat-receive-interval="20000"/>
 </websocket:message-broker>

If you setup security in ActiveMQ, you will also need to include the attributes client-login, client-passcode, system-login and system-passcode for connecting to  the broker on behalf of the client and the application respectively. By default, they are set as “guest”

Putting in altogether

That’s it. Now to test it out. first fire off ActiveMQ, you should see in the logger messages something like this:

INFO | Listening for connections at: stomp://messageserver:61613
INFO | Connector stomp://localhost:61613 Started

Now start the web server, I am using jetty 9. You should see the following logger message from reactor-tcp:

INFO : 12 Apr 2014 10:47:13,023 (reactor-tcp-io-3) reactor.tcp.netty.NettyTcpClient - CONNECT: [id: 0x71a8bfb1, /127.0.0.1:49617 =>       localhost/127.0.0.1:61613]
 ...
[INFO] Started Jetty Server

Now we are ready to send some messages to the full broker. I use the hello world application in my previous blog to send a message to the topic “greetings”. You should then see the topic created in the ActiveMQ admin console, e.g. http://localhost:8161/admin/topics.jsp.

Using Spring 4 WebSocket, sockJS and Stomp support to implement two way server client communication

One exciting new feature of Spring 4 is the support for WebSocket, SockJS and STOMP messaging. This allows two way communication between the server and its clients in a Spring MVC web application using the standard point-to-point and publish-subscribe messaging protocols. In this post, I will demonstrate how to set up a basic boilerplate project to start using this new feature. It is in part based on this article.

Maven Setup

First we need to add the Spring messaging modules in the POM file:

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-messaging</artifactId>
 <version>4.0.0.RELEASE</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-websocket</artifactId>
 <version>4.0.0.RELEASE</version>
 </dependency>

Spring MVC Configuration

Next, we need to add the message broker config to the Spring MVC config XML file.

<beans
 ...
 xmlns:websocket="http://www.springframework.org/schema/websocket"
 xsi:schemaLocation="
 ...
 http://www.springframework.org/schema/websocket
 http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd">
<websocket:message-broker application-destination-prefix="/app">
       <websocket:stomp-endpoint path="/hello">
            <websocket:sockjs/>
       </websocket:stomp-endpoint>
       <websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
<!-- Other MVC config omitted here-->

The main thing here is the set up of the message broker for handling the message exchange between the server and its clients. This is done via the <message-broker> and its child tags. The tag <websocket:simple-broker> indicates we are using in-memory message broker.

It is easy to understand together with the server and client codes so I will include them below first before attempting to give a bit more explanations by cross-referencing the client and server codes.

Spring MVC Controller

Below is my Spring MVC Controller

 @Controller
 public class MessageController {
      @MessageMapping("/hello")
      @SendTo("/topic/greetings")
      public Greeting greeting(HelloMessage message) throws Exception {
           return new Greeting("Hello, " + message.getName() + "!");
     }
}

The method argument HelloMessage and output Greeting are just POJOs representing the body of the messages being sent and returned.

public class Greeting {
    private String content;
    public Greeting(String content) {
           this.content = content;
    }
    public String getContent() {
      return content;
    }
}
public class HelloMessage {
    private String name;
    public String getName() {
        return name;
    }
}

Client sockJS and STOMP codes

On the client side, I use the sockJS protocol fallback option as outlined in the Spring documentation. The javascript codes are included below

// Create stomp client over sockJS protocol (see Note 1)
 var socket = new SockJS("/hello");
 var stompClient = Stomp.over(socket);

 // callback function to be called when stomp client is connected to server (see Note 2)
 var connectCallback = function() {
      alert("connected!");
      stompClient.subscribe('/topic/greetings', function(greeting){
           alert(JSON.parse(greeting.body).content);
      });
 }; 

 // callback function to be called when stomp client could not connect to server (see Note 3)
 var errorCallback = function(error) {
      // display the error's message header:
      alert(error.headers.message);
 };

 // Connect as guest (Note 4)
 stompClient.connect("guest", "guest", connectCallback, errorCallback);

Note

  1. The client starts by create a sockJS by specifying the endpoint (ie. /hello) to connect to and then a stomp client is created over the socket. The endpoint here should match that defined in the Spring MVC configuration in the lines. Note also the 2nd line referring to sockJS.

    <websocket:stomp-endpoint path=”/hello”>
    <websocket:sockjs/>
    </websocket:stomp-endpoint>

  2. Then a callback function is created and assigned to a variable connectCallback. This is called when a successful connection is made by the stomp client. This allows us to start making subscriptions to messages (as in codes, repeated below) and sending messages. Note the subscription is for the topic “/topic/greetings”

    stompClient.subscribe(‘/topic/greetings’, function(greeting){
    alert(JSON.parse(greeting.body).content);
    });

  3. A error callback function is defined if stomp client fails to connect to server.
  4. This line makes the connection registering the callback functions.

Now we are ready to send messages from the client, e.g. using the following javascript function

// function to send message
 function fnSayHi() {
       stompClient.send("/app/hello", {}, JSON.stringify({ 'name': 'Joe' }));
 }

The message will be sent to the Spring MVC message handler method greeting() as defined via the annotation @MessageMapping(“/hello”).

 <websocket:message-broker application-destination-prefix=”/app”>

Note the prefix “/app” is defined in the Spring config as  application-destination-prefix attribute of the message broker: Note also, the use of @SendTo annotation to direct the message to a given destination. I repeat the controller method below

 @MessageMapping("/hello")
 @SendTo("/topic/greetings")
 public Greeting greeting(HelloMessage message) throws Exception {
      return new Greeting("Hello, " + message.getName() + "!");
 }

That’s it for now.