Chapter 7. test

7.1. test: Integration Testing with Profiles

7.2. Introduction

In this lab you will refactor the RewardNetworkTests using Spring’s system test support library to simplify and improve the performance of your system. You will then use Spring profiles to define multiple tests using different implementations of the AccountRepository, RestaurantRepository and RewardRepository for different environments.

What you will learn:

  1. The recommended way of system testing an application configured by Spring
  2. How to write multiple test scenarios

Specific subjects you will gain experience with:

  1. JUnit 5
  2. Spring’s TestContext framework
  3. Spring Profiles

Estimated time to complete: 30 minutes

7.3. Quick Instructions

Quick instructions for this exercise have been embedded within the lab materials in the form of TODO comments. To display them, open the Tasks view (Window >> Show View >> Tasks (not Task List)). Use the view’s small down arrow to select a Configure Contents…​ menu, you’ll find the instructions are easy to follow if you configure TODOs to display On any element in the same project.

Occasionally, TODO’S defined within XML files disappear from the Tasks view (i.e. gaps in the number sequence). To correct this, go to Preferences >> General >> Editors >> Structured Text Editor >> Task Tags pane. Check Enable searching for Task Tags and click Clean and Redetect Tasks. On the Filters tab, ensure XML content type is checked.

7.4. Detailed Instructions

7.4.1. Refactor to use Spring’s TestContext framework (TODO 01)

In rewards.RewardNetworkTests we setup our test-environment using Spring in the @BeforeEach setUp method. Instead we are going to use Spring’s test-extension. Comment out the @BeforeEach method - highlight the method and use CTRL+SHIFT+C (on a Mac use COMMAND+SHIFT+C). Now run the test. You will get a Red bar because the rewardNetwork field is null.

Spring’s TestContext framework offers full support for JUnit 5 via the SpringExtension class.

Your next step is to tell JUnit to run your test using JUnit and then refactor your test as necessary to work with it. You will need to add 2 annotations just like the examples in the notes.

[Tip] Tip

In RewardNetworkTests you will need to add an @SpringJUnitConfig annotation and also use @Autowired.

Now when you run your test the test runner’s setup logic will use auto-wiring on your test class to set values from the ApplicationContext. This means your rewardNetwork will be assigned to the RewardNetwork bean from the context automatically.

Re-run your test in Eclipse and verify you get a green bar. If so, the rewardNetwork field is being set properly for you. If you don’t see green, try to figure out where the problem lies. If you can’t figure it out, ask the instructor to help you find the issue.

[Note] Note

When you have the green bar, you’ve successfully reconfigured the rewards integration test, and at the same time simplified your system test by leveraging Spring’s test support. In addition, the performance of your system test has potentially improved as the ApplicationContext is now created once per test case run (and cached) instead of once per test method. This test only has one method so it doesn’t make any difference here.

We can clear up what we no longer need by deleting the context field and removing the @BeforeEach and @AfterEach methods.

Rerun the test and check that "Clearing restaurant cache" appears on the console - this means the @PreDestroy method is still being invoked by Spring.

7.4.2. Configure Repository Implementations using Profiles

We are now going to modify the test to use different repository implementations - either Stubs or using JDBC.

First we are going to use the stub repositories in /src/test/java/rewards/internal. We need to make them Spring beans by annotating them as repository components. Follow TODO 02 and annotate the stub classes with @Repository.

If you run RewardNetworkTests again, it should fail because you have multiple beans of the same type - the original JDBC implementations and now the stubs. To fix this we will introduce two profiles: * The stub repositories will belong to the "stub" profile * The JDBC repositories to the "jdbc" profile.

Follow all the TODO 03 steps and use the @Profile annotation to put all the repositories in this project into their correct profile - there are 6 repository classes to annotate in total.

Finally annotate the RewardNetworkTests class with @ActiveProfiles to make "stub" the active profile. Rerun the test - it should work now. Check the console to see that the stub repository implementations are being used. Notice that the embedded database is also being created even though we don’t use it. We will fix this soon.

Switch the active-profile to "jdbc" instead (TODO 04). Rerun the test - it should still work. Check the console again to see that the JDBC repository implementations are being used.

7.4.3. Switching between Development and Production Profiles

Profiles allow different configurations for different environments such as development, testing, QA (Quality Assurance), UAT (User Acceptance Testing), production and so forth. In the last step we will introduce two new profiles: "jdbc-dev" and "jdbc-production". In both cases we will be using the JDBC implementations of our repositories so two profiles will need to be active at once.

The difference between development and production is typically different infrastructure. In this case we are going to swap between an in-memory test database and the "real" database defined as a JNDI resource.

Modify TestInfrastructureDevConfig.java so that all the beans are members of the profile called "jdbc-dev" (TODO 05).

Does RewardNetworkTests still run OK? Why not?

Fix the test by adding the "jdbc-dev" profile to the @ActiveProfiles annotation in RewardNetworkTests (TODO 06). Remember you will need to retain the "jdbc" profile as well. Rerun the test - it should work again.

We have already setup the production dataSource for you using a JNDI lookup (see TODO 07). We have used a standalone JNDI implementation - normally JNDI would be provided by your JEE container (such as Tomcat or tc Server).

Change the active profile of RewardNetworkTests from "jdbc-dev" to "jdbc-production". Rerun the test, it should still work. To see what has changed, look at the console and you will see logging from an extra bean called SimpleJndiHelper. Switch the profile back to "jdbc-dev" and rerun. Check the console and note that the SimpleJndiHelper is no longer used.

7.4.4. Optional Step - Further Refactoring

When no class or XML file is specified, Spring’s test framework will look for an inner static class marked with @Configuration (If none is found it will then look for an XML file name of <Classname>-context.xml). Since the TestInfrastructureConfig class is so small anyway, copy the entire class definition, including annotations, to an inner static class within the test class. Then remove the configuration class reference from the @SpringJUnitConfig annotation (no property in the brackets).

This is an example of convention over configuration. Does the test still run?

[Note] Note

When you copy the TestInfrastructureConfig class into RewardNetworkTests, remember to make it static - refer to example in notes if unsure.