Verifying DateTime and Date with Hamcrest

twittergoogle_plusrss

Facebooktwittergoogle_plusredditlinkedinmail

Since I started diving into automated testing and practicing TDD, verification of date values was pain. Luckily there is nice library for legacy Date and new Java 8 DateTime APIs, which cures this pain.

If you belong to healthier part of Java development community and practicing unit testing on daily basis, you probably are aware of Hamcrest Java library. It can make your tests much more readable. It’s architecture is very modular and is used by various other testing libraries.

Major part of it’s flexibility is it concept of Matcher. I am not going to dive into this concept now. If you are not familiar, just take a quick look at Hamcrest tutorial. One of the matcher you can plug into your testing toolbox is library hamcrest-date. With this library we can easily test that date was generated within certain range:

    @Test
    public void validateDate() {
        //GIVEN
        Date expectedDate = new Date();

        //WHEN
        Date actualDate = new Date();

        //THEN
        assertThat(actualDate, DateMatchers.within(2, ChronoUnit.SECONDS, expectedDate));
    }

We can do that also for Java 8 types:

    @Test
    public void validateDateTime() {
        //GIVEN
        LocalDateTime expectedDateTime = LocalDateTime.now();

        //WHEN
        LocalDateTime actualDateTime = LocalDateTime.now();

        //THEN
        assertThat(actualDateTime, LocalDateTimeMatchers.within(2, ChronoUnit.SECONDS, expectedDateTime));
    }

Or pick various exotic verifications hamcrest-core library provides:

    @Test
    public void validateZonedDateTime() {
        //GIVEN
        ZonedDateTime expectedDateTime = ZonedDateTime.of(2016, 3, 20, 13, 3, 0, 0, ZoneId.of("GMT+1"));

        //WHEN
        ZonedDateTime actualDateTime = ZonedDateTime.of(2016, 3, 20, 13, 3, 0, 0, ZoneId.of("GMT-0"));

        //THEN
        assertThat(actualDateTime, ZonedDateTimeMatchers.sameDay(expectedDateTime));
        assertThat(actualDateTime, ZonedDateTimeMatchers.after(expectedDateTime));
        assertThat(actualDateTime, ZonedDateTimeMatchers.isSunday());
        assertThat(actualDateTime, ZonedDateTimeMatchers.isMarch());
    }

Kudos to creator for this nice little library. This example is hosted in Github.

twittergoogle_plusrss

Create UML diagrams with simple DSL – PlantUML

twittergoogle_plusrss

Facebooktwittergoogle_plusredditlinkedinmail

Historically I disliked creating design UML diagrams, because the tooling for it mostly requires drawing boxes and connecting them with lines or arrows. I find such workflow very time consuming and wasteful. This short blog post will describe much better way to create UML diagrams.

But first of all, you may argue that UML importance in today’s agile software development world is fading out. I would agree partially.

Creating UML Class Diagrams

From my point of view UML class diagrams are needed very rarely. I can find only two use cases for it:

  • To analyze existing code base
  • To communicate suggested design with the team

When you need to analyze existing code base, class diagrams can be easily generated by your IDE from the existing code. IntelliJ idea provides this as part of payed version:UML

To find out how to create such diagram, read this IntelliJ IDEA documentation. It is also easy with Eclipse or it’s flavors (e.g. Spring Tool Suite):

UML

You just need to use plugin called ObjectAid.

When we need to sketch class diagram upfront to consult design with the team, I would suggest to create prototype with desired class structure instead and generate class diagram from the live code (again with IDE). You may find some important facts about the design while creating prototype. There’s no need to create logic and tests, just APIs of the design.

And of course when you don’t need such generated diagram anymore, just wipe it out. Low level design may change easily and you don’t want to keep outdated diagrams around. You can generate new diagram from live code easily.

Non-Class diagrams

So when we need Class diagram, it’s smart to generate it from live code. But what about other UML diagrams like:

  • Structure diagrams (except Class diagram)
  • Behavioral diagrams
  • Interaction diagrams (especially useful is Sequence diagram from this bucket)

I personally created only few Use case and Flow chart diagrams during my career. But Sequence and Component diagrams are very useful to document high-level design decisions. Of course you don’t want to have them very detailed, but at high level designer intentions and considerations are sometimes good to document.

But the tool that is used for designing these diagrams has to be very easy to use and flexible. Sorry Visio or Enterprise Architect, I don’t include you into this bucket (although I have to admin I haven’t use EA for long time).

My tool of choice it PlantUML.  We can define design with very simple DSL. Therefore instead of connecting boxes and lines on canvas, we can focus on design itself. It can be used as standalone application but it also have impressive list of integrations. So you can easily use it with your IntelliJ IDEA, Eclipse or Spring Tool Suite.

Very compelling fact for me, is that DSL which defines diagrams can live with the project code-base and also be handled by source control system, with all the goodness it brings. This is simple screenshot from IntelliJ:

UML

twittergoogle_plusrss
Selenium Travis

Speed up Gradle build on TravisCI

twittergoogle_plusrss

Facebooktwittergoogle_plusredditlinkedinmail

I was recently speeding up Gradle build on TravisCI for one Dotsub project. It builds Spring Boot based project (written in Java), but also uses Gulp sub-tasks on top of NodeJS to bundle front-end code and assets. This blog post is about sharing these small build optimization hints. They are also shared in this Github repository.

Default TravisCI configuration for Gradle

By default, when Travis detects Gradle as build system it configures these two phases by default:

install: gradle assemble
build: gradle check

So as part of your build, Gradle is executed twice by default. And it also does make some sense when you take a look at how Gradle Java plugin works:

Gradle Java plugin tasks

As you can see assemble and check phases has only few initial phases in common. There isn’t much tasks redundancy if we run assemble and check separately.

There is also Gradle UP-TO-DATE feature. It means Gradle is smart enough to mark task as UP-TO-DATE after first execution. Redundant execution of this task can be than skipped if its input doesn’t change. Of course it works across various separate build executions. Let’s take a look at what happen when we build mentioned example project with this example configuration. Link to build is here.

$ ./gradlew assemble
:compileJava
:processResources
:classes
:findMainClass
:jar
:bootRepackage
:assemble

BUILD SUCCESSFUL

Total time: 21.0 secs

$ ./gradlew check
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
2016-02-06 11:26:06.883  INFO 2455 --- [       Thread-6] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@295c442d: startup date [Sat Feb 06 11:25:47 UTC 2016]; root of context hierarchy
:check

BUILD SUCCESSFUL

Total time: 55.216 secs

Notice that Full elapsed time was 1 min 49 seconds.

As you can see execution of check task didn’t have to run tasks compileJava, processResources and classes again, because they were executed previously as part of assemble task.

So TravisCI default configuration may seem reasonable for Gradle.

Merge install and script phase

But even if Gradle makes best effort to reduce overhead, the overhead is there. As Gradle Java Plugin tasks visualization showed earlier in the blog post, we can cover assemble and check tasks with build task. So why should we run two Gradle processes in single build when single process execution is enough?

Also mentioned two default tasks may be fine for Continuous Integration workflow, but modern developers and teams doesn’t stop there. Every company that takes software development seriously wants to incorporate Continuous Delivery or Continuous Deployment. So we want to assemble, check, build and deploy all our assets in one build pipeline. So why would we want to run two Gradle processes?

Let’s do this:

install: echo "skip 'gradle assemble' step"
script: gradle build --continue

In Travis Install phase we override default assemble task and run full Gradle build as one process. Let take a look at timings of such build for very same example project. Build link is here.

$ echo "skip './gradlew assemble' step"
skip './gradlew assemble' step
$ ./gradlew build --continue
:compileJava
:processResources
:classes
:findMainClass
:jar
:bootRepackage
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
2016-01-31 20:19:37.202  INFO 2353 --- [       Thread-6] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@46441da4: startup date [Sun Jan 31 20:19:24 UTC 2016]; root of context hierarchy
:check
:build

BUILD SUCCESSFUL

Total time: 36.926 secs

Notice that Full elapsed time was 59 seconds.

So even if Gradle tries to optimize tasks as much as possible, there is noticeable overhead running two separate Gradle processed instead of two.

Cache Gradle and NodeJS dependencies

Second hint to speed up TravisCI build for Gradle can be found in TravisCI docs itself. It involves Gradle dependencies caching.

As I mentioned at the beginning, project I work on also involves Gulp/NodeJS build. Therefore it make sense to put also NPM dependencies into TravisCI cache. Gradle integration with Gulp can be done via Gradle Gulp plugin. This plugin locates NPM global dependencies into $HOME/.gradle/nodejs and local NPM depndencies into node_modules folder.

So full of Gradle caching configuration for our build looks like this:

before_cache:
  - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
cache:
  directories:
    - $HOME/.gradle/caches/
    - $HOME/.gradle/wrapper/
    - $HOME/.gradle/nodejs/
    - node_modules

Don’t ask me why we need to remove lock in before_cache section. That is taken from mentioned TravisCI docs. Rest of the configuration caches all Gradle and NPM dependencies between builds.

Here is link to build without this caching: 1 min 56 seconds

Here is link to build with this caching: 59 seconds. This example build doesn’t contain Gradle Gulp plugin, so saving from NPM dependencies caching are not included.

Conclusion

These two simple tricks cut off around 4 minutes from Dotsub project build I mentioned. Pretty nice when it’s for free. If you want to mess with this example, you can look at this Github repo.

 

twittergoogle_plusrss
Selenium tests on Gradle in Travis

Run Selenium tests on TravisCI

twittergoogle_plusrss

Facebooktwittergoogle_plusredditlinkedinmail

Stack of application I am currently working on at Dotsub is based on Java/Spring Boot back-end and React/Redux front-end. To have confidence that this application works end to end, we are using Selenium tests. It is very easy to run them as part application build, because Spring Boot testing support allows to run full application as part of application build. We use Gradle as main build system and it is all running on Travis continuous integration server. To demonstrate this approach for end to end testing I created small Hello World project on GitHub.

Build

Build system of choice is Gradle. Creation of following Gradle script was very easy, because I used Spring Initializr:

buildscript {
	ext {
		springBootVersion = '1.3.2.RELEASE'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
	baseName = 'blog-2016-01-selenium-on-travis'
	version = '0.0.1-SNAPSHOT'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
	mavenCentral()
}


dependencies {
	compile('org.springframework.boot:spring-boot-starter')
	compile('org.springframework.boot:spring-boot-starter-web')
	testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile("org.seleniumhq.selenium:selenium-firefox-driver:2.49.0")
    testCompile('org.seleniumhq.selenium:selenium-support:2.49.0')
}

task wrapper(type: Wrapper) {
	gradleVersion = '2.9'
}

The only additional dependencies against generated script (by Spring Initializr) are Selenium, Firefox Selenium driver and Spring Starter Web. Adding spring-boot-starter-web into build will transform our project into web application with embedded Tomcat servlet container.

Hello World Application

To demonstrate Selenium Tests automation, I created very simple application code. First of all we need Spring Boot main class:

package net.lkrnac.blog.seleniumontravis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String... args){
        SpringApplication.run(Application.class);
    }
}

It is very standard Spring Boot construct for Spring context initialization.  Annotation @SpringBootApplication turns on Spring Boot auto-configuration. It sets up most sensible defaults for out application, which is most importantly embedded servlet container in this case.

Second part of our simple application code is front-end code. For demonstration purposes this simplest React example (taken from React getting started guide) will be enough:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>

<body>
<div id="hello" />

<script type="text/babel">
    ReactDOM.render(
    <h1>Hello, world!</h1>, document.getElementById('hello') );
</script>
</body>

</html>

It uses Babel to transpile JSX inline and pulls React libraries from CDN. It doesn’t do any AJAX calls to server. We also didn’t create any Spring controller for serving requests. It is because goal of this example is to demonstrate Selenium testing against React+Spring Boot app, therefore I skipped communication between client and server.

This simple HTML + React Hello World page is located in file src/main/resources/static/index.html, where it will be picked up by Spring Boot and exposed as default web page content when request hits root URL of embedded servlet container.

Simple Selenium Test

Following listing shows how can we approach selenium testing against Spring Boot application:

import net.lkrnac.blog.seleniumontravis.Application;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.By;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.io.IOException;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
@WebIntegrationTest
public class ApplicationTest {
    private static FirefoxDriver driver;

    @BeforeClass
    public static void setUp() throws IOException {
        driver = new FirefoxDriver();
    }

    @Test
    public void contextLoads() {
        driver.get("https://localhost:8080");
        WebDriverWait wait = new WebDriverWait(driver, 10);
        wait.until(ExpectedConditions.textToBePresentInElementLocated(
                By.id("hello"), "Hello, world!"));
    }

    @AfterClass
    public static void tearDown() {
        driver.quit();
    }
}

Before test, it starts Firefox Selenium driver. During test it visits default specified address https://localhost:8080. This should hit our index.html. Next phase of the test is waiting for Hello, world! header to be rendered on screen. After this happens, test is done and Selenium driver is closed. When we run this test locally, we can see following pop-up appear on the screen.

Selenoum Tests

 

Travis Configuration

Last piece of this example it TravisCI configuration manifest. Relevant parts of it are here:

language: java
jdk:
  - oraclejdk8

before_script:
  - "export DISPLAY=:99.0"
  - "sh -e /etc/init.d/xvfb start"
  - sleep 3 # give xvfb some time to start

script: ./gradlew build --continue

First of all we specify that Java 8 is our language of choice. Before script part is taken from TravisCI docs. It starts Xvfb (X virtual frame buffer), which simulates X11 display server on Linux machine without screen. This allows render our site virtually, because TravisCI build machine contains installation of Firefox by default. This configuration is enough for Selenium tests. In script phase we start full Gradle build.

Possible Travis problems and solution

All this configuration may be enough for you to start simple Selenium testing again React application. But default Firefox version on TravisCI machine is 31.0 ESR. This is quite old version and some of our React pages may not be rendered correctly during the build. Luckily TravisCI allows to update Firefox version with this simple manifest declaration:

addons:
  firefox: "44.0"

This installs new version of Firefox on TravisCI, but unfortunately it is not enough because of this open TravisCI issue. Consequence is that default configuration of Selenium Firefox driver configuration use old Firefox binary instead of new one.

But when I executed which firefox command on Travis, it was pointing to new binary file. Therefore I used this Selenium Driver initialization to pick up newer Firefox binary on Travis:

import net.lkrnac.blog.seleniumontravis.Application;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.By;
import org.openqa.selenium.firefox.FirefoxBinary;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
@WebIntegrationTest
public class UseNewFirefoxOnTravisTest {
    private static FirefoxDriver driver;

    @BeforeClass
    public static void setUp() throws IOException {
        String travisCiFlag = System.getenv().get("TRAVIS");
        FirefoxBinary firefoxBinary = "true".equals(travisCiFlag)
                ? getFirefoxBinaryForTravisCi()
                : new FirefoxBinary();

        driver = new FirefoxDriver(firefoxBinary, new FirefoxProfile());
    }

    private static FirefoxBinary getFirefoxBinaryForTravisCi() throws IOException {
        String firefoxPath = getFirefoxPath();
        Logger staticLog = LoggerFactory.getLogger(UseNewFirefoxOnTravisTest.class);
        staticLog.info("Firefox path: " + firefoxPath);

        return new FirefoxBinary(new File(firefoxPath));
    }

    private static String getFirefoxPath() throws IOException {
        ProcessBuilder pb = new ProcessBuilder("which", "firefox");
        pb.redirectErrorStream(true);
        Process process = pb.start();
        try (InputStreamReader isr = new InputStreamReader(process.getInputStream(), "UTF-8");
             BufferedReader br = new BufferedReader(isr)) {
            return br.readLine();
        }
    }

    @Test
    public void contextLoads() {
        driver.get("https://localhost:8080");
        WebDriverWait wait = new WebDriverWait(driver, 10);
        wait.until(ExpectedConditions.textToBePresentInElementLocated(
                By.id("hello"),
                "Hello, world!")
        );
    }

    @AfterClass
    public static void tearDown() {
        driver.quit();
    }
}

Testing logic is the same as for example test we already introduced. Different is initialization of Firefox Selenium driver. In this case we first recognize if we are running in Travis environment via environment variable TRAVIS. If we are not running in Travis, we use default Firefox driver initialization.

If we are running in TravisCI build, we use standard Java class ProcessBuilder to execute Linux command which firefox in separate process and grab it’s output. This gives us path of newer Firefox binary. Based on this path, we initialize Firefox Selenium driver and are good to automatically run Selenium test against latest Firefox on TravisCI build machine.

Source code for this example is located in GitHub.

twittergoogle_plusrss
mock Spring bean, encapsulate spring bean

How to mock Spring bean (version 2)

twittergoogle_plusrss

Facebooktwittergoogle_plusredditlinkedinmail

EDIT: As of Spring Boot 1.4.0, faking of Spring Beans is supported natively via annotation @MockBean. Read Spring Boot docs for more info.

About a year ago, I wrote a blog post how to mock Spring Bean. Patterns described there were little bit invasive to the production code.  As one of the readers Colin correctly pointed out in comment, there is better alternative to spy/mock Spring bean based on @Profile annotation. This blog post is going to describe this technique. I used this approach with success at work and also in my side projects.

Note that widespread mocking in your application is often considered as design smell.

Introducing production code

First of all we need code under test to demonstrate mocking. We will use these simple classes:

@Repository
public class AddressDao {
	public String readAddress(String userName) {
		return "3 Dark Corner";
	}
}

@Service
public class AddressService {
	private AddressDao addressDao;
	
	@Autowired
	public AddressService(AddressDao addressDao) {
		this.addressDao = addressDao;
	}
	
	public String getAddressForUser(String userName){
		return addressDao.readAddress(userName);
	}
}

@Service
public class UserService {
	private AddressService addressService;

	@Autowired
	public UserService(AddressService addressService) {
		this.addressService = addressService;
	}
	
	public String getUserDetails(String userName){
		String address = addressService.getAddressForUser(userName);
		return String.format("User %s, %s", userName, address);
	}
}

Of course this code doesn’t make much sense, but will be good to demonstrate how to mock Spring bean. AddressDao just returns string and thus simulates read from some data source. It is autowired into AddressService. This bean is autowired into UserService, which is used to construct string with user name and address.

Notice that we are using constructor injection as field injection is considered as bad practice. If you want to enforce constructor injection for your application, Oliver Gierke (Spring ecosystem developer and Spring Data lead) recently created very nice project Ninjector.

Configuration which scans all these beans is pretty standard Spring Boot main class:

@SpringBootApplication
public class SimpleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SimpleApplication.class, args);
    }
}

Mock Spring bean (without AOP)

Let’s test the AddressService class where we mock AddressDao. We can create this mock via Spring’ @Profiles and @Primary annotations this way:

@Profile("AddressService-test")
@Configuration
public class AddressDaoTestConfiguration {
	@Bean
	@Primary
	public AddressDao addressDao() {
		return Mockito.mock(AddressDao.class);
	}
}

This test configuration will be applied only when Spring profile AddressService-test is active. When it’s applied, it registers bean of type AddressDao, which is mock instance created by Mockito. @Primary annotation tells Spring to use this instance instead of real one when somebody autowire AddressDao bean.

Test class is using JUnit framework:

@ActiveProfiles("AddressService-test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(SimpleApplication.class)
public class AddressServiceITest {
	@Autowired 
	private AddressService addressService;

	@Autowired
	private AddressDao addressDao;

	@Test
	public void testGetAddressForUser() {
		// GIVEN
		Mockito.when(addressDao.readAddress("john"))
			.thenReturn("5 Bright Corner");

		// WHEN 
		String actualAddress = addressService.getAddressForUser("john");
  
		// THEN   
		Assert.assertEquals("5 Bright Corner", actualAddress);
	}
}

We activate profile AddressService-test to enable AddressDao mocking. Annotation @RunWith is needed for Spring integration tests and @SpringApplicationConfiguration defines which Spring configuration will be used to construct context for testing. Before the test, we autowire instance of AddressService under test and AddressDao mock.

Subsequent testing method should be clear if you are using Mockito. In GIVEN phase, we record desired behavior into mock instance. In WHEN phase, we execute testing code and in THEN phase, we verify if testing code returned value we expect.

Spy on Spring Bean (without AOP)

For spying example, will be spying on AddressService instance:

@Profile("UserService-test")
@Configuration
public class AddressServiceTestConfiguration {
	@Bean
	@Primary
	public AddressService addressServiceSpy(AddressService addressService) {
		return Mockito.spy(addressService);
	}
}

This Spring configuration will be component scanned only if profile UserService-test will be active. It defines primary bean of type AddressService. @Primary tells Spring to use this instance in case two beans of this type are present in Spring context.  During construction of this bean we autowire existing instance of AddressService from Spring context and use Mockito’s spying feature. The bean we are registering is effectively delegating all the calls to original instance, but Mockito spying allows us to verify interactions on spied instance.

We will test behavior of UserService this way:

@ActiveProfiles("UserService-test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(SimpleApplication.class)
public class UserServiceITest {
	@Autowired
	private UserService userService;

	@Autowired
	private AddressService addressService;
 
	@Test
	public void testGetUserDetails() {
		// GIVEN - Spring scanned by SimpleApplication class

		// WHEN
		String actualUserDetails = userService.getUserDetails("john");
 
		// THEN
		Assert.assertEquals("User john, 3 Dark Corner", actualUserDetails);
		Mockito.verify(addressService).getAddressForUser("john");
	}
}

For testing we activate UserService-test profile so our spying configuration will be applied. We autowire UserService which is under test and AddressService, which is being spied via Mockito.

We don’t need to prepare any behavior for testing in GIVEN phase. WHEN phase is obviously executing code under test. In THEN phase we verify if testing code returned value we expect and also if addressService call was executed with correct parameter.

Problems with Mockito and Spring AOP

Let say now we want to use Spring AOP module to handle some cross-cutting concerns. For example to log calls on our Spring beans this way:

package net.lkrnac.blog.testing.mockbeanv2.aoptesting;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;
    
@Aspect
@Component
@Slf4j
@Profile("aop") //only for example purposes
public class AddressLogger {
    @Before("execution(* net.lkrnac.blog.testing.mockbeanv2.beans.*.*(..))")
    public void logAddressCall(JoinPoint jp){
        log.info("Executing method {}", jp.getSignature());
    }
}

This AOP Aspect is applied before call on Spring beans from package net.lkrnac.blog.testing.mockbeanv2. It is using Lombok’s annotation @Slf4j to log signature of called method. Notice that this bean is created only when aop profile is defined. We are using this profile to separate AOP and non-AOP testing examples. In a real application you wouldn’t want to use such profile.

We also need to enable AspectJ for our application, therefore all the following examples will be using this Spring Boot main class:

@SpringBootApplication
@EnableAspectJAutoProxy
public class AopApplication {
    public static void main(String[] args) {
        SpringApplication.run(AopApplication.class, args);
    }
}

 

AOP constructs are enabled by @EnableAspectJAutoProxy.

But such AOP constructs may be problematic if we combine Mockito for mocking with Spring AOP. It is because both use CGLIB to proxy real instances and when Mockito proxy is wrapped into Spring proxy, we can experience type mismatch problems. These can be mitigated by configuring bean’s scope with ScopedProxyMode.TARGET_CLASS, but Mockito verify() calls still fail with NotAMockException. Such problems can be seen if we enable aop profile for UserServiceITest.

Mock Spring bean proxied by Spring AOP

To overcome these problems, we will wrap mock into this Spring bean:

package net.lkrnac.blog.testing.mockbeanv2.aoptesting;

import org.mockito.Mockito;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Repository;

import lombok.Getter;
import net.lkrnac.blog.testing.mockbeanv2.beans.AddressDao;

@Primary
@Repository
@Profile("AddressService-aop-mock-test")
public class AddressDaoMock extends AddressDao{
    @Getter
    private AddressDao mockDelegate = Mockito.mock(AddressDao.class);
    
    public String readAddress(String userName) {
        return mockDelegate.readAddress(userName);
    }
}

@Primary annotation makes sure that this bean will take precedence before real AddressDao bean during injection. To make sure it will be applied only for specific test, we define profile AddressService-aop-mock-test for this bean. It inherits AddressDao class, so that it can act as full replacement of that type.

In order to fake behavior, we define mock instance of type AddressDao, which is exposed via getter defined by Lombok’s @Getter annotation. We also implement readAddress() method which is expected to be called during test. This method just delegates the call to mock instance.

The test where this mock is used can look like this:

@ActiveProfiles({"AddressService-aop-mock-test", "aop"})
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(AopApplication.class)
public class AddressServiceAopMockITest {
    @Autowired
    private AddressService addressService; 

    @Autowired
    private AddressDao addressDao;
    
    @Test
    public void testGetAddressForUser() {
        // GIVEN
        AddressDaoMock addressDaoMock = (AddressDaoMock) addressDao;
        Mockito.when(addressDaoMock.getMockDelegate().readAddress("john"))
            .thenReturn("5 Bright Corner");
 
        // WHEN 
        String actualAddress = addressService.getAddressForUser("john");
 
        // THEN  
        Assert.assertEquals("5 Bright Corner", actualAddress);
    }
}

In the test we define AddressService-aop-mock-test profile to activate AddressDaoMock and aop profile to activate AddressLogger AOP aspect. For testing, we autowire testing bean addressService and its faked dependency addressDao. As we know, addressDao will be of type AddressDaoMock, because this bean was marked as @Primary. Therefore we can cast it and record behavior into mockDelegate.

When we call testing method, recorded behavior should be used because we expect testing method to use AddressDao dependency.

Spy on Spring bean proxied by Spring AOP

Similar pattern can be used for spying the real implementation. This is how our spy can look like:

package net.lkrnac.blog.testing.mockbeanv2.aoptesting;

import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import lombok.Getter;
import net.lkrnac.blog.testing.mockbeanv2.beans.AddressDao;
import net.lkrnac.blog.testing.mockbeanv2.beans.AddressService;

@Primary
@Service
@Profile("UserService-aop-test")
public class AddressServiceSpy extends AddressService{
    @Getter
    private AddressService spyDelegate;
    
    @Autowired
    public AddressServiceSpy(AddressDao addressDao) {
        super(null);
        spyDelegate = Mockito.spy(new AddressService(addressDao));
    }
    
    public String getAddressForUser(String userName){
        return spyDelegate.getAddressForUser(userName);
    }
}

As we can see this spy is very similar to AddressDaoMock. But in this case real bean is using constructor injection to autowire its dependency. Therefore we’ll need to define non-default constructor and do constructor injection also. But we wouldn’t pass injected dependency into parent constructor.

To enable spying on real object, we construct new instance with all the dependencies, wrap it into Mockito spy instance and store it into spyDelegate property. We expect call of method getAddressForUser() during test, therefore we delegate this call to spyDelegate. This property can be accessed in test via getter defined by Lombok’s @Getter annotation.

Test itself would look like this:

@ActiveProfiles({"UserService-aop-test", "aop"})
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(AopApplication.class)
public class UserServiceAopITest {
    @Autowired
    private UserService userService;

    @Autowired
    private AddressService addressService;
    
    @Test
    public void testGetUserDetails() {
        // GIVEN
        AddressServiceSpy addressServiceSpy = (AddressServiceSpy) addressService;

        // WHEN
        String actualUserDetails = userService.getUserDetails("john");
  
        // THEN 
        Assert.assertEquals("User john, 3 Dark Corner", actualUserDetails);
        Mockito.verify(addressServiceSpy.getSpyDelegate()).getAddressForUser("john");
    }
}

It is very straight forward. Profile UserService-aop-test ensures that AddressServiceSpy will be scanned. Profile aop ensures same for AddressLogger aspect. When we autowire testing object UserService and its dependency AddressService, we know that we can cast it to AddressServiceSpy and verify the call on its spyDelegate property after calling the testing method.

Fake Spring bean proxied by Spring AOP

It is obvious that delegating calls into Mockito mocks or spies complicates the testing. These patterns are often overkill if we simply need to fake the logic. We can use such fake in that case:

@Primary
@Repository
@Profile("AddressService-aop-fake-test")
public class AddressDaoFake extends AddressDao{
    public String readAddress(String userName) {
        return userName + "'s address";
    }
}

and used it for testing this way:

@ActiveProfiles({"AddressService-aop-fake-test", "aop"})
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(AopApplication.class)
public class AddressServiceAopFakeITest {
    @Autowired
    private AddressService addressService; 

    @Test
    public void testGetAddressForUser() {
        // GIVEN - Spring context
 
        // WHEN 
        String actualAddress = addressService.getAddressForUser("john");
 
        // THEN  
        Assert.assertEquals("john's address", actualAddress);
    }
}

I don’t think this test needs explanation.

Source code for these examples is hosted on Github.

twittergoogle_plusrss