leave

© Image by Margarethe Pfründer-Sonn↗

In my previous post I’ve written that you should avoid starting a large spring context for tests. But sometimes, you need to start the complete spring context for testing your stuff.

How can I avoid waiting on every test execution?

Is there a way I can have a unit test feeling on integration tests?

Yes, there is a way!

But it comes with one constraint: Write strict black box tests! No direct access to repositories, no mockito mocks or something like this.

Basic idea

The basic idea: Start the service once and keep it running all the time.

Don’t use spring↗ in the tests. Just call the public APIs of the service.

Each execution of a test method does not require a restart of the spring context, so it is really fast.

But my current tests are all @SpringBootTests!

Most people inherit integration tests from a base test class; let’s call it BaseIT. Maybe this base class looks like


    @SpringBootTest
    public class BaseIT {
        
        @LocalServerPort
        protected int serverPort;
    
    }

You now provide a new class BaseDevelopmentIT. It has the same field that is used by the derived classes to obtain the spring server’s port, but it points to your continuously running instance on localhost. It does not start the service’s spring context.


    public class BaseDevelopmentIT {
        
        protected int serverPort = 8080; // port of spring boot's http endpoints on your local machine
    
    }

In your integration test, you can switch from extends BaseIT to extends BaseDevelopmentIT.

Then you can quickly run and change and re-run the test until all the test methods are correct without restarting the spring context.

After you’ve finished coding the test, you switch it back to extends BaseIT for continuous integration.

Advanced test

But at some time, you will need to change the implementation because your test has shown you an error in the implementation. Then you still need to write the code, stop and restart the service and wait for spring to initialize the context.

But there is help. JRebel↗ brings hot code deployment to spring boot↗ (Something we already had decades ago with plain apache tomcat↗ or EJB).

So you can do real TDD with integration tests with spring boot and JRebel and short turnaround cycles:

Start spring boot using JRebel. Keep it running all the time.

  1. Code the test
  2. Build and run the test -> red
  3. Code the implementation (no restart!)
  4. Build and run the test -> green

-> repeat

Screencast

Constraints

  • no direct injection of services or repositories into the test class
  • no mockito↗ mocks or spies of spring beans

Happy Testing!

Any comments or suggestions? Leave an issue or a pull request or discuss on reddit!