提交 cf2aed9d 编写于 作者: B Brian Clozel

Add a dateValue HeaderResultMatcher

HTTP headers such as "Expires", "Last-Modified" all use date
strings like "Tue, 21 Jul 2015 10:00:00 GMT". Prior to this commit,
there was no way to match those header values, besides formatting dates
manually.

This commit introduces a new HeaderResultMatcher to test those date
headers using a long timestamp:

```
this.mockMvc.perform(get("/persons/1").header("If-Modified-Since", now))
  .andExpect(status().isNotModified())
  .andExpect(header().dateValue("Last-Modified", timestamp));
```

Issue: SPR-13263
上级 ed20b377
......@@ -24,6 +24,11 @@ import org.springframework.test.web.servlet.ResultMatcher;
import static org.hamcrest.MatcherAssert.*;
import static org.springframework.test.util.AssertionErrors.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* Factory for response header assertions.
* <p>An instance of this class is usually accessed via
......@@ -31,6 +36,7 @@ import static org.springframework.test.util.AssertionErrors.*;
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @author Brian Clozel
* @since 3.2
*/
public class HeaderResultMatchers {
......@@ -96,4 +102,26 @@ public class HeaderResultMatchers {
};
}
/**
* Assert the primary value of the named response header as a date String,
* using the preferred date format described in RFC 7231.
* <p>The {@link ResultMatcher} returned by this method throws an {@link AssertionError}
* if the response does not contain the specified header, or if the supplied
* {@code value} does not match the primary value.
*
* @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
* @since 4.2
*/
public ResultMatcher dateValue(final String name, final long value) {
return new ResultMatcher() {
@Override
public void match(MvcResult result) {
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
assertTrue("Response does not contain header " + name, result.getResponse().containsHeader(name));
assertEquals("Response header " + name, format.format(new Date(value)), result.getResponse().getHeader(name));
}
};
}
}
......@@ -19,13 +19,13 @@ package org.springframework.test.web.servlet.samples.standalone.resultmatchers;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.Person;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import static org.hamcrest.CoreMatchers.*;
......@@ -34,11 +34,17 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* Examples of expectations on response header values.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @author Brian Clozel
*/
public class HeaderAssertionTests {
......@@ -50,6 +56,12 @@ public class HeaderAssertionTests {
private final long currentTime = System.currentTimeMillis();
private String now;
private String oneMinuteAgo;
private String oneSecondLater;
private MockMvc mockMvc;
private PersonController personController;
......@@ -57,6 +69,11 @@ public class HeaderAssertionTests {
@Before
public void setup() {
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
this.now = dateFormat.format(new Date(currentTime));
this.oneMinuteAgo = dateFormat.format(new Date(currentTime - (1000 * 60)));
this.oneSecondLater = dateFormat.format(new Date(currentTime + 1000));
this.personController = new PersonController();
this.personController.setStubTimestamp(currentTime);
this.mockMvc = standaloneSetup(this.personController).build();
......@@ -64,32 +81,38 @@ public class HeaderAssertionTests {
@Test
public void stringWithCorrectResponseHeaderValue() throws Exception {
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
.andExpect(header().string(LAST_MODIFIED, String.valueOf(currentTime)));
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, oneMinuteAgo))//
.andExpect(header().string(LAST_MODIFIED, now));
}
@Test
public void stringWithMatcherAndCorrectResponseHeaderValue() throws Exception {
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
.andExpect(header().string(LAST_MODIFIED, equalTo(String.valueOf(currentTime))));
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, oneMinuteAgo))//
.andExpect(header().string(LAST_MODIFIED, equalTo(now)));
}
@Test
public void dateValueWithCorrectResponseHeaderValue() throws Exception {
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, oneMinuteAgo))//
.andExpect(header().dateValue(LAST_MODIFIED, currentTime));
}
@Test
public void longValueWithCorrectResponseHeaderValue() throws Exception {
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
.andExpect(header().longValue(LAST_MODIFIED, currentTime));
this.mockMvc.perform(get("/persons/1"))//
.andExpect(header().longValue("X-Rate-Limiting", 42));
}
@Test
public void stringWithMissingResponseHeader() throws Exception {
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))//
.andExpect(status().isNotModified())//
.andExpect(header().string("X-Custom-Header", (String) null));
}
@Test
public void stringWithMatcherAndMissingResponseHeader() throws Exception {
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))//
.andExpect(status().isNotModified())//
.andExpect(header().string("X-Custom-Header", nullValue()));
}
......@@ -97,7 +120,7 @@ public class HeaderAssertionTests {
@Test
public void longValueWithMissingResponseHeader() throws Exception {
try {
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, now))//
.andExpect(status().isNotModified())//
.andExpect(header().longValue("X-Custom-Header", 99L));
......@@ -129,26 +152,28 @@ public class HeaderAssertionTests {
@Test
public void stringWithIncorrectResponseHeaderValue() throws Exception {
long unexpected = currentTime + 1000;
assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, String.valueOf(unexpected)), unexpected);
assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, oneSecondLater), oneSecondLater);
}
@Test
public void stringWithMatcherAndIncorrectResponseHeaderValue() throws Exception {
long unexpected = currentTime + 1000;
assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, equalTo(String.valueOf(unexpected))),
unexpected);
assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, equalTo(oneSecondLater)), oneSecondLater);
}
@Test
public void longValueWithIncorrectResponseHeaderValue() throws Exception {
public void dateValueWithIncorrectResponseHeaderValue() throws Exception {
long unexpected = currentTime + 1000;
assertIncorrectResponseHeaderValue(header().longValue(LAST_MODIFIED, unexpected), unexpected);
assertIncorrectResponseHeaderValue(header().dateValue(LAST_MODIFIED, unexpected), oneSecondLater);
}
private void assertIncorrectResponseHeaderValue(ResultMatcher resultMatcher, long unexpected) throws Exception {
@Test(expected = AssertionError.class)
public void longValueWithIncorrectResponseHeaderValue() throws Exception {
this.mockMvc.perform(get("/persons/1")).andExpect(header().longValue("X-Rate-Limiting", 1));
}
private void assertIncorrectResponseHeaderValue(ResultMatcher resultMatcher, String unexpected) throws Exception {
try {
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, oneMinuteAgo))//
.andExpect(resultMatcher);
fail(EXPECTED_ASSERTION_ERROR_MSG);
......@@ -162,8 +187,8 @@ public class HeaderAssertionTests {
// We don't use assertEquals() since we cannot control the formatting
// produced by JUnit or Hamcrest.
assertMessageContains(e, "Response header " + LAST_MODIFIED);
assertMessageContains(e, String.valueOf(unexpected));
assertMessageContains(e, String.valueOf(currentTime));
assertMessageContains(e, unexpected);
assertMessageContains(e, now);
}
}
......@@ -186,12 +211,12 @@ public class HeaderAssertionTests {
}
@RequestMapping("/persons/{id}")
@ResponseBody
public Person showEntity(@PathVariable long id, WebRequest request) {
if (request.checkNotModified(calculateLastModified(id))) {
return null;
}
return new Person("Jason");
public ResponseEntity<Person> showEntity(@PathVariable long id, WebRequest request) {
return ResponseEntity
.ok()
.lastModified(calculateLastModified(id))
.header("X-Rate-Limiting", "42")
.body(new Person("Jason"));
}
private long calculateLastModified(long id) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册