7

We are using MockMvc Framework to test Spring Controllers with JUnit. Controller returns a DefferedResult.

The mockmvc.perform looks like bellow

mockMvc.perform(post("/customer")
                .accept(APPLICATION_JSON)
                .header(AUTH_TOKEN_KEY, "xyz")
                .header(FROM_KEY, "email@gmail.com")
                .content(json)
                .contentType(APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(request().asyncStarted());

And it takes a lot of time. We are Using embedded cassandra and because of that it takes a lot of time.

I tried this as well, but it's same.

MvcResult mvcResult = mockMvc.perform(post("/customer")
            .accept(APPLICATION_JSON)
            .header(AUTH_TOKEN_KEY, "xyz")
            .header(FROM_KEY, "email@gmail.com")
            .content(json)
            .contentType(APPLICATION_JSON))
            .andReturn();

mockMvc.perform(asyncDispatch(mvcResult))
            .andExpect(status().isOk())
            .andExpect(request().asyncStarted());

I've hundreds of tests, because of which the build process is really slow.

Is there a way, using JUnit I can say perform the request and wait for response in another thread to assert the results, Or anyother good way of speeding it up.

Thanks

Mritunjay
  • 25,338
  • 7
  • 55
  • 68
  • I think the best way to get an answer for this is to create a full end-to-end snippet e.g. hosted on a GitHub page somewhere so people can pull down have a play about with and see the performance stats themselves. Due to the nature of this (integration testing) I doubt if you'll get an accurate and helpful answer without supplying some code. – bobmarksie May 29 '16 at 09:33

3 Answers3

1

Do you really need the Cassandra/Persistence-Layer of your application for this test?

If the anser is no, or if the answer is no a wide array of tests cases, than you could inject another persitsence repoitory when running tests. To achive this, you could use Spring's built in Profile functionality and annotate your tests accordingly, for example:

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfile("StubPersistence")
public class WebLayerIntegrationTests {
...
}

You could then have a stubbed version of your Cassanda-Repository for your tests, that are allowed to work with static data:

@Profiles("StubPersistence")
@Repository
public class StubCassandaRepository {
    ...
}

This class could be backed by a simple data structure like a HashSet or similar, depdends on your use case. The possibility of this approach depdens heavy on your software architecture, so it might not be possible if you can't stub out your Cassandra depdencies.

I also wonder if you really need hundreds of test, that need your complete application, including the web layer. You can of course significantly speed up your tests by favoring Unit-Tests over Integration-Tests, so that you don't need to initiliaze the Spring-Context. Depends on your application and software architecture as well.

There will also be some testing improvements in Spring-Boot 1.4 that will allow you to specifically initilize single slices of your application for testing (like Persistence, or Web-Layer): https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4

So, my best advice is: If you want to test your Controllers, test only your Controllers and not your Persistence-Layer, stub it out instead. If you want to test your Persistence-Layer, start with the interfaces of your Persistence-Layer, don't use your Controllers as the test-interface.

Kevin Wittek
  • 1,369
  • 9
  • 26
1

As I mentioned in my question We are Using embedded cassandra and because of that it takes a lot of time.

I tried looking things in cassandra.yaml file and changed the line below.

commitlog_sync_batch_window_in_ms: 90

to

commitlog_sync_batch_window_in_ms: 1

That's all and the build time was reduced from 30 minutes to 2 minutes.

From cassandra.yaml comments:-

It will wait up to commitlog_sync_batch_window_in_ms milliseconds for other writes, before performing the sync.

After reducing this time the wait time was reduced and build time got reduced.

Mritunjay
  • 25,338
  • 7
  • 55
  • 68
0

Are you doing the following?

  1. Running as a "Spring Integration Test"? e.g.

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = CassandraConfig.class)
    public class BookRepositoryIntegrationTest {
      //
    }
    
  2. Are you starting and stopping the embedded Casandra server using the @BeforeClass / @AfterClass` annotations? e.g.

    @BeforeClass
    public static void startCassandraEmbedded() { 
        EmbeddedCassandraServerHelper.startEmbeddedCassandra(); 
        Cluster cluster = Cluster.builder()
          .addContactPoints("127.0.0.1").withPort(9142).build();
        Session session = cluster.connect(); 
    }
    
    ... and ...
    
    @AfterClass
    public static void stopCassandraEmbedded() {
        EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
    }
    

See http://www.baeldung.com/spring-data-cassandra-tutorial for more information

bobmarksie
  • 3,282
  • 1
  • 41
  • 54
  • yeah, we are running it `Spring Integration Test` and we are not *starting and stopping* cassandra before every class, because I think cassandra server startup and running migrations will increase the time. – Mritunjay May 13 '16 at 12:34
  • OK great, have you identified where the slowness is? Is it possibly `DeferredResult`? Is it handled currently on another thread? – bobmarksie May 13 '16 at 12:50
  • nope, actually we do all the task and then get the result, wrap it in `DeferredResult` and return it from method. – Mritunjay May 13 '16 at 14:08