Refactor async result handling in Spring MVC Test

This change removes the use of a CountDownLatch to wait for the
asynchronously computed controller method return value. Instead we
check in a loop every 200 milliseconds if the result has been set.
If the result is not set within the specified amount of time to wait
an IllegalStateException is raised.

Additional changes:
 - Use AtomicReference to hold the async result
 - Remove @Ignore annotations on AsyncTests methods
 - Remove checks for the presence of Servlet 3

Issue: SPR-11516
This commit is contained in:
Rossen Stoyanchev
2014-03-05 12:48:33 -05:00
parent e50cff47c1
commit 74de35df1e
7 changed files with 53 additions and 145 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.*;
/**
@@ -37,80 +38,23 @@ public class DefaultMvcResultTests {
private DefaultMvcResult mvcResult;
private CountDownLatch countDownLatch;
@Before
public void setup() {
ExtendedMockHttpServletRequest request = new ExtendedMockHttpServletRequest();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAsyncStarted(true);
this.countDownLatch = mock(CountDownLatch.class);
this.mvcResult = new DefaultMvcResult(request, null);
this.mvcResult.setAsyncResultLatch(this.countDownLatch);
}
@Test
public void getAsyncResultWithTimeout() throws Exception {
long timeout = 1234L;
given(this.countDownLatch.await(timeout, TimeUnit.MILLISECONDS)).willReturn(true);
this.mvcResult.getAsyncResult(timeout);
verify(this.countDownLatch).await(timeout, TimeUnit.MILLISECONDS);
public void getAsyncResultSuccess() throws Exception {
this.mvcResult.setAsyncResult("Foo");
assertEquals("Foo", this.mvcResult.getAsyncResult(10 * 1000));
}
@Test
public void getAsyncResultWithTimeoutNegativeOne() throws Exception {
given(this.countDownLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)).willReturn(true);
this.mvcResult.getAsyncResult(-1);
verify(this.countDownLatch).await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
}
@Test
public void getAsyncResultWithoutTimeout() throws Exception {
given(this.countDownLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)).willReturn(true);
this.mvcResult.getAsyncResult();
verify(this.countDownLatch).await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
}
@Test
public void getAsyncResultWithTimeoutZero() throws Exception {
@Test(expected = IllegalStateException.class)
public void getAsyncResultFailure() throws Exception {
this.mvcResult.getAsyncResult(0);
verifyZeroInteractions(this.countDownLatch);
}
@Test(expected=IllegalStateException.class)
public void getAsyncResultAndTimeOut() throws Exception {
this.mvcResult.getAsyncResult(-1);
verify(this.countDownLatch).await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
}
private static class ExtendedMockHttpServletRequest extends MockHttpServletRequest {
private boolean asyncStarted;
private AsyncContext asyncContext;
public ExtendedMockHttpServletRequest() {
super();
this.asyncContext = mock(AsyncContext.class);
given(this.asyncContext.getTimeout()).willReturn(new Long(DEFAULT_TIMEOUT));
}
@Override
public void setAsyncStarted(boolean asyncStarted) {
this.asyncStarted = asyncStarted;
}
@Override
public boolean isAsyncStarted() {
return this.asyncStarted;
}
@Override
public AsyncContext getAsyncContext() {
return asyncContext;
}
}
}

View File

@@ -133,7 +133,7 @@ public class StubMvcResult implements MvcResult {
}
@Override
public Object getAsyncResult(long timeout) {
public Object getAsyncResult(long timeToWait) {
return null;
}

View File

@@ -59,23 +59,19 @@ public class AsyncTests {
}
@Test
@Ignore
public void testCallable() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/1").param("callable", "true"))
.andDo(print())
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(new Person("Joe")))
.andReturn();
this.mockMvc.perform(asyncDispatch(mvcResult))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}"));
}
@Test
@Ignore
public void testDeferredResult() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/1").param("deferredResult", "true"))
.andExpect(request().asyncStarted())
@@ -90,7 +86,6 @@ public class AsyncTests {
}
@Test
@Ignore
public void testDeferredResultWithSetValue() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/1").param("deferredResultWithSetValue", "true"))
.andExpect(request().asyncStarted())