finnetrolle
finnetrolle

Reputation: 295

Testing Spring boot REST resource issues

There is a microservice made with Spring Boot. It consist of Jetty, Jersey, Jackson and Liquibase. One of tasks of this service is to receive some data via REST and return response. This operation goes through next units:

This application works fine, but now I need to add some tests for every module. Usually I use Mockito to test units, so I test my service with mocked MyRepository (Mockito @Mock annotation + when() method). I want to test MyResource.java the same way.

I tried to use TestRestTemplate way to test with spring-boot-starter-test and my test class looks like this:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebIntegrationTest(randomPort = true)
public class MyResourceTest {
  @Value("${local.server.port}")
  private int port;

  private String getBaseUrl() {
    return "http://localhost:" + port;
  }

  @Test
  public void test() {
    final TestRestTemplate restTemplate = new TestRestTemplate();
    assertEquals(restTemplate.postForEntity(getBaseUrl() + "/test", null, ResponseObject.class).getBody(), new ResponseObject());
  }
}

And there are two problems. First - when my test is running, they run up whole spring application, so my liquibase scripts is trying to find database and this is a very long-time process. Second - I can't replace MyService class with Mockito proxy.

I tried to find some manuals about best practices in testing spring boot REST applications and I found MockMvc-based way, but it looks like don't run up server to run test. Can you please share your way to test REST resource in spring boot?

Upvotes: 0

Views: 1052

Answers (2)

Marco Blos
Marco Blos

Reputation: 1066

I run all my tests over 6 classes of configuration/support.

AbstractTest to configure core of tests

@ActiveProfiles(resolver = TestActiveProfilesResolver.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@IntegrationTest
@SpringApplicationConfiguration(classes = Application.class)
public abstract class AbstractTest {
   ...
}

AbstractRepositoryTest to test my repositories over jdbc+jdbi

@Transactional
public abstract class AbstractRepositoryTest<R> extends AbstractTest implements Repositories {

    @Inject
    private ObjectMapper mapper;

    @Inject
    protected Repositories repositories;

    private final Class<R> repositoryType;

    ...
}

AbstractServiceTest to test my services, this class contains the core of my service's test

public abstract class AbstractServiceTest {
    ...
}

AbstractIntegrationTest contains the core of my integration tests, utils methods to test controller's, etc.

public abstract class AbstractIntegrationTest extends AbstractTest {
    ...
}

Application provides to AbstractTest a context to run tests, this classe is same class that I use to run my spring boot application

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

    @Override
    protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
    ...
}

And finnaly the TestActiveProfilesResolver, that provide the profile to match application-test.properties, It's necessary because exist the open issue on JIRA, here

public class TestActiveProfilesResolver implements ActiveProfilesResolver {

    @Override
    public String[] resolve(final Class<?> testClass) {
        final String activeProfile = System.getProperty("spring.profiles.active");
        return new String[] {activeProfile == null ? "test" : activeProfile};
    }

}

Sometimes my tests extend AbstractRepositoryTest, AbstractIntegrationTests or AbstractServiceTest, it depends on what I want. This configuration solved all my problems to test services, controllers etc.

Upvotes: 1

MockMvc is the prefered solution for your problem. It runs the spring boot application but 'mocks' the request,so it does not really run over http but behaves as such. You can get all beans of your application injected into your test class using @Autowired, so you can mock spring beans there.

Upvotes: 2

Related Questions