spring-boot Testing in Spring Boot How to Test a Simple Spring Boot Application

Help us to keep this website almost Ad Free! It takes only 10 seconds of your time:
> Step 1: Go view our video on YouTube: EF Core Bulk Extensions
> Step 2: And Like the video. BONUS: You can also share it!

Example

We have a sample Spring boot application which stores user data in MongoDB and we are using Rest services to retrieve data

First there is a domain class i.e. POJO

@Document
public class User{
    @Id
    private String id;

    private String name;

}

A corresponding repository based on Spring Data MongoDB

public interface UserRepository extends MongoRepository<User, String> {
}

Then Our User Controller

@RestController
class UserController {
 
    @Autowired
    private UserRepository repository;
 
    @RequestMapping("/users")
    List<User> users() {
        return repository.findAll();
    }
 
    @RequestMapping(value = "/Users/{id}", method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    void delete(@PathVariable("id") String id) {
        repository.delete(id);
    }
 
    // more controller methods
}

And finally our Spring Boot Application

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

If, let’s say that John Cena, The Rock and TripleHHH were the only three users in the database, a request to /users would give the following response:

$ curl localhost:8080/users
[{"name":"John Cena","id":"1"},{"name":"The Rock","id":"2"},{"name":"TripleHHH","id":"3"}]

Now to test the code we will verify that the application work

@RunWith(SpringJUnit4ClassRunner.class)   // 1
@SpringApplicationConfiguration(classes = Application.class)   // 2
@WebAppConfiguration   // 3
@IntegrationTest("server.port:0")   // 4
public class UserControllerTest {

    @Autowired   // 5
    UserRepository repository;

    User cena;
    User rock;
    User tripleHHH;

    @Value("${local.server.port}")   // 6
    int port;

    @Before
    public void setUp() {
        // 7
        cena = new User("John Cena");
        rock = new User("The Rock");
        tripleHHH = new User("TripleHH");

        // 8
        repository.deleteAll();
        repository.save(Arrays.asList(cena, rock, tripleHHH));

        // 9
        RestAssured.port = port;
    }

    // 10
    @Test
    public void testFetchCena() {
        String cenaId = cena.getId();

        when().
                get("/Users/{id}", cenaId).
        then().
                statusCode(HttpStatus.SC_OK).
                body("name", Matchers.is("John Cena")).
                body("id", Matchers.is(cenaId));
    }

    @Test
    public void testFetchAll() {
        when().
                get("/users").
        then().
                statusCode(HttpStatus.SC_OK).
                body("name", Matchers.hasItems("John Cena", "The Rock", "TripleHHH"));
    }

    @Test
    public void testDeletetripleHHH() {
        String tripleHHHId = tripleHHH.getId();

        when()
                .delete("/Users/{id}", tripleHHHId).
        then().
                statusCode(HttpStatus.SC_NO_CONTENT);
    }
}

Explanation

  1. Like any other Spring based test, we need the SpringJUnit4ClassRunner so that an application context is created.
  2. The @SpringApplicationConfiguration annotation is similar to the @ContextConfiguration annotation in that it is used to specify which application context(s) that should be used in the test. Additionally, it will trigger logic for reading Spring Boot specific configurations, properties, and so on.
  3. @WebAppConfiguration must be present in order to tell Spring that a WebApplicationContext should be loaded for the test. It also provides an attribute for specifying the path to the root of the web application.
  4. @IntegrationTest is used to tell Spring Boot that the embedded web server should be started. By providing colon- or equals-separated name-value pair(s), any environment variable can be overridden. In this example, the "server.port:0" will override the server’s default port setting. Normally, the server would start using the specified port number, but the value 0 has a special meaning. When specified as 0, it tells Spring Boot to scan the ports on the host environment and start the server on a random, available port. That is useful if we have different services occupying different ports on the development machines and the build server that could potentially collide with the application port, in which case the application will not start. Secondly, if we create multiple integration tests with different application contexts, they may also collide if the tests are running concurrently.
  5. We have access to the application context and can use autowiring to inject any Spring bean.
  6. The @Value("${local.server.port}”) will be resolved to the actual port number that is used.
  7. We create some entities that we can use for validation.
  8. The MongoDB database is cleared and re-initialized for each test so that we always validate against a known state. Since the order of the tests is not defined, chances are that the testFetchAll() test fails if it is executed after the testDeletetripleHHH() test.
  9. We instruct Rest Assured to use the correct port. It is an open source project that provides a Java DSL for testing restful services
  10. Tests are implemented by using Rest Assured. we can implement the tests using the TestRestTemplate or any other http client, but I use Rest Assured because we can write concise documentation using RestDocs


Got any spring-boot Question?