Add header name to ex. msg in HeaderResultMatchers
Prior to this commit, the longValue() in HeaderResultMatchers added the expected response header name to generated assertion failure messages; however, both string() methods did not. This made it more difficult to analyze the cause of failed tests. Furthermore, while investigating a solution for this issue it became apparent that longValue() throws a NullPointerException if the response does not contain the specified header. This commit addresses these issues as follows: - All methods in HeaderResultMatchers now include the response header name in generated assertion failure messages. - HeaderResultMatchers.longValue() now avoids NullPointerExceptions by explicitly asserting that the response contains the specified header. - The unit tests in HeaderAssertionTests have been expanded to test most foreseeable use cases. Issue: SPR-10659
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.test.web.servlet.result;
|
||||
|
||||
import static org.springframework.test.util.AssertionErrors.assertEquals;
|
||||
import static org.springframework.test.util.AssertionErrors.assertTrue;
|
||||
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
@@ -28,6 +29,7 @@ import org.springframework.test.web.servlet.ResultMatcher;
|
||||
* class is usually accessed via {@link MockMvcResultMatchers#header()}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
public class HeaderResultMatchers {
|
||||
@@ -41,36 +43,45 @@ public class HeaderResultMatchers {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a response header with the given Hamcrest {@link Matcher}.
|
||||
* Assert the primary value of the named response header with the given
|
||||
* Hamcrest {@link Matcher}.
|
||||
*/
|
||||
public ResultMatcher string(final String name, final Matcher<? super String> matcher) {
|
||||
return new ResultMatcher() {
|
||||
|
||||
@Override
|
||||
public void match(MvcResult result) {
|
||||
assertThat("Response header", result.getResponse().getHeader(name), matcher);
|
||||
assertThat("Response header " + name, result.getResponse().getHeader(name), matcher);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the primary value of a response header as a {@link String}.
|
||||
* Assert the primary value of the named response header as a {@link String}.
|
||||
*/
|
||||
public ResultMatcher string(final String name, final String value) {
|
||||
return new ResultMatcher() {
|
||||
|
||||
@Override
|
||||
public void match(MvcResult result) {
|
||||
assertEquals("Response header", value, result.getResponse().getHeader(name));
|
||||
assertEquals("Response header " + name, value, result.getResponse().getHeader(name));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the primary value of a response header as a {@link Long}.
|
||||
* Assert the primary value of the named response header as a {@code long}.
|
||||
*
|
||||
* <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.
|
||||
*/
|
||||
public ResultMatcher longValue(final String name, final long value) {
|
||||
return new ResultMatcher() {
|
||||
|
||||
@Override
|
||||
public void match(MvcResult result) {
|
||||
assertTrue("Response does not contain header " + name, result.getResponse().containsHeader(name));
|
||||
assertEquals("Response header " + name, value, Long.parseLong(result.getResponse().getHeader(name)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -16,72 +16,154 @@
|
||||
|
||||
package org.springframework.test.web.servlet.samples.standalone.resultmatchers;
|
||||
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
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.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
|
||||
|
||||
/**
|
||||
* Examples of expectations on response header values.
|
||||
*
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class HeaderAssertionTests {
|
||||
|
||||
private static final String EXPECTED_ASSERTION_ERROR_MSG = "Should have thrown an AssertionError";
|
||||
|
||||
private static final String IF_MODIFIED_SINCE = "If-Modified-Since";
|
||||
|
||||
private static final String LAST_MODIFIED = "Last-Modified";
|
||||
|
||||
private final long currentTime = System.currentTimeMillis();
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private PersonController personController;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.personController = new PersonController();
|
||||
this.personController.setStubTimestamp(currentTime);
|
||||
this.mockMvc = standaloneSetup(this.personController).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValue() throws Exception {
|
||||
long currentTime = new Date().getTime();
|
||||
this.personController.setStubTimestamp(currentTime);
|
||||
this.mockMvc.perform(get("/persons/1").header("If-Modified-Since", currentTime - (1000 * 60)))
|
||||
.andExpect(header().string("Last-Modified", String.valueOf(currentTime)));
|
||||
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)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongValue() throws Exception {
|
||||
long currentTime = new Date().getTime();
|
||||
this.personController.setStubTimestamp(currentTime);
|
||||
this.mockMvc.perform(get("/persons/1").header("If-Modified-Since", currentTime - (1000 * 60)))
|
||||
.andExpect(header().longValue("Last-Modified", currentTime));
|
||||
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))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatcher() throws Exception {
|
||||
long currentTime = new Date().getTime();
|
||||
this.personController.setStubTimestamp(currentTime);
|
||||
this.mockMvc.perform(get("/persons/1").header("If-Modified-Since", currentTime))
|
||||
.andExpect(status().isNotModified())
|
||||
.andExpect(header().string("Last-Modified", nullValue()));
|
||||
public void longValueWithCorrectResponseHeaderValue() throws Exception {
|
||||
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
|
||||
.andExpect(header().longValue(LAST_MODIFIED, currentTime));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringWithMissingResponseHeader() throws Exception {
|
||||
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
|
||||
.andExpect(status().isNotModified())//
|
||||
.andExpect(header().string(LAST_MODIFIED, (String) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringWithMatcherAndMissingResponseHeader() throws Exception {
|
||||
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
|
||||
.andExpect(status().isNotModified())//
|
||||
.andExpect(header().string(LAST_MODIFIED, nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void longValueWithMissingResponseHeader() throws Exception {
|
||||
try {
|
||||
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
|
||||
.andExpect(status().isNotModified())//
|
||||
.andExpect(header().longValue(LAST_MODIFIED, 99L));
|
||||
|
||||
fail(EXPECTED_ASSERTION_ERROR_MSG);
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
if (EXPECTED_ASSERTION_ERROR_MSG.equals(e.getMessage())) {
|
||||
throw e;
|
||||
}
|
||||
assertEquals("Response does not contain header " + LAST_MODIFIED, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringWithIncorrectResponseHeaderValue() throws Exception {
|
||||
long unexpected = currentTime + 1;
|
||||
assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, String.valueOf(unexpected)), unexpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringWithMatcherAndIncorrectResponseHeaderValue() throws Exception {
|
||||
long unexpected = currentTime + 1;
|
||||
assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, equalTo(String.valueOf(unexpected))),
|
||||
unexpected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void longValueWithIncorrectResponseHeaderValue() throws Exception {
|
||||
long unexpected = currentTime + 1;
|
||||
assertIncorrectResponseHeaderValue(header().longValue(LAST_MODIFIED, unexpected), unexpected);
|
||||
}
|
||||
|
||||
private void assertIncorrectResponseHeaderValue(ResultMatcher resultMatcher, long unexpected) throws Exception {
|
||||
try {
|
||||
this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
|
||||
.andExpect(resultMatcher);
|
||||
|
||||
fail(EXPECTED_ASSERTION_ERROR_MSG);
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
if (EXPECTED_ASSERTION_ERROR_MSG.equals(e.getMessage())) {
|
||||
throw e;
|
||||
}
|
||||
// [SPR-10659] Ensure that the header name is included in the message
|
||||
//
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMessageContains(AssertionError error, String expected) {
|
||||
String message = error.getMessage();
|
||||
assertTrue("Failure message should contain: " + expected, message.contains(expected));
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Controller
|
||||
private static class PersonController {
|
||||
|
||||
private long timestamp;
|
||||
|
||||
|
||||
public void setStubTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user