DEV Community

Cover image for Optimizing Spring Integration Tests: 7 Annotations That May Be Slowing You Down
Willian Ferreira Moya
Willian Ferreira Moya

Posted on • Edited on • Originally published at springmasteryhub.com

Optimizing Spring Integration Tests: 7 Annotations That May Be Slowing You Down

Introduction:

We all know that Integration tests are not the fastest to run, they surely take longer than unit tests.

The reason for that is that you have to start up Spring context to run the tests. The bigger your application gets the more time your application takes to start.

That can make your team impatient to wait for the tests to run, and start skipping them. Also, this could even prevent your team from writing more integration test scenarios.

But do you know that depending on what you are doing, you can make your tests take longer than they should?

Some simple annotations can restart Spring context making everything slower!

So let’s find out why this happens and what can be causing that.

Why does this happen?

The @SpringBootTest annotation in Spring allows your test suite to use the same context for all tests.

Then it will reuse the same context for all your integration tests. That can save some execution time since it doesn’t have to start spring again.

If you keep seeing the Spring banner and startup logs in your test logs, something might be causing a restart.

What could it be?

This happens because there’s something in your tests that is dirtying the context. This will trigger an automatic Spring context restart before your next test class.

7 Annotations that trigger spring context restart

1 - @SpyBean:

Problem:

Using a spy (partial mock) in an existing bean in a context that is already running. This will make your test restart spring context to replace the original bean with the spy bean.

Solution:

Try to define the bean in a @Configuation class specific to your tests. Or make a central point like a superclass that has where you define all your test annotations together with the @SpringBootTest. You make this spy bean an attribute for the superclass and make it available for the test class. That will make the spy available for your test class by inheritance. But this spy will be available for all classes that inherit this superclass. This is not the most beautiful solution, but it works. You can always redesign your code to avoid having to use the @SpyBean annotation

2 - @AutoConfigureWiremock (it happens in version 2.35.0, maybe it is fixed in a recent version):

By default, @AutoConfigureWiremock will be configured with @AutoConfigureWiremock(port=0). It will assign a random port for Wiremock when running tests.

Problem:

Using @AutoConfigureWiremock(port=1212) in class ATest and @AutoConfigureWiremock(port=3232) in class BTest, Spring will restart the context.

If you keep changing the stubs path, you’ll face issues too. ATest uses: @AutoConfigureWiremock(port=0, stubs="/src/resources/mappings/ATest"). BTest uses a different path: @AutoConfigureWiremock(port=0, stubs="/src/resources/mappings/BTest").

Solution:

Keep all your tests using the default configuration of wiremock @AutoConfigureWiremock(port=0).

Avoid changing the stubs folder definition from test to test.

If neither is possible, you could create a new wiremock instance from test to test or class to class. Using this way you can avoid using the annotation @AutoConfigureWiremock. And you can make every instance point to a different stub folder.

But be careful, by doing that you will have to maintain a fixed por in the application.properties. Now spring is not controlling wiremock anymore, and that is why it will not trigger a context restart.

3 - @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD):

This annotation closes and recreates context in a test environment. And can be used in some moments like:

  • before each test method
  • after the test method
  • before the test class
  • after the test class

Problem:

The focus of the annotation already is to restart the context. Developers usually use this annotation because the tests work when running alone but fail when running the entire class. That can happen because your tests are not isolated and the execution of one affects the other's behavior.

Solution:

Write your test scenarios isolated from each other, and make each scenario have its unique data. If you need a database table with some data, create it before the test and delete it at the end of the test.

Remember when your system is running in a production server your users are doing requests altogether. You do not restart your spring application from each request you receive, so why should you do it in tests?

4- @MockBean

Problem:

When @MockBeanappears in a class, the ApplicationContext cache gets marked as dirty. Therefore the runner will clean the cache after the test class is done

Solution:

You can follow the same steps to fix the @SpyBean issue.

5 - @TestPropertySource:

It defines configuration sources with higher precedence than any other source in the project. You can also inline some properties or specify a different location for the properties.

Problem:

By doing that in some test classes you will be forcing Spring to restart to replace this property with some that has a higher priority. Or to include some new properties, or even to look for properties in a different folder.

Solution:

Avoid this by creating test application properties that are all set to your test needs. Spring will load once the defined properties are exclusive to your tests.

6 - @ActiveProfiles("test")

This enables you to change the profile from the application, and load different beans, properties, and so on.

Problem:

If you change this configuration each test your Spring context will always restart. It will apply the changes and load up the beans, properties, and configurations to the specific profile.

Solution:

Do you need to change profiles? Can you aggregate your configurations into a single one? If you can, do it. If not, you can reduce the damage by creating a central point, like a superclass that will group the test execution by profile.

7 - Annotations inside third-party libraries.

Problem

Some third-party libraries can contain some auto configurations on their own and can cause you some issues. And this can be tricky to find.

Solution

There’s no easy way to find that, so investigate and try to find it. Make sure if it’s not something you could remove, or exclude some part of the dependency that you are not using.+

Conclusion

So that’s it, start looking at your integrated tests and see what you find. If you find some of these problems, try applying the solutions presented here.

By avoiding these mistakes, your Spring application will start only once and cache the context. This can significantly reduce the runtime of your integration tests.

Have you encountered other issues that slow down your integration tests? Feel free to share your experiences and solutions.

Follow me on social media (or here in dev.to) to learn more about efficient testing and improving applications:

Willian Moya (@WillianFMoya) / X (twitter.com)

Willian Ferreira Moya | LinkedIn

Top comments (0)