package net.petrikainulainen.spring.datajpa.controller;
import net.petrikainulainen.spring.datajpa.context.TestContext;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.context.MessageSource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.when;
/**
* An abstract base class for all controller unit tests.
* @author Petri Kainulainen
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestContext.class})
public abstract class AbstractTestController {
protected static final String ERROR_MESSAGE = "errorMessage";
protected static final String FEEDBACK_MESSAGE = "feedbackMessage";
private static final String FLASH_ERROR_MESSAGE = "errorMessage";
private static final String FLASH_FEEDBACK_MESSAGE = "feedbackMessage";
private static final String VIEW_REDIRECT_PREFIX = "redirect:";
private MessageSource messageSourceMock;
@Resource
private Validator validator;
@Before
public void setUp() {
messageSourceMock = mock(MessageSource.class);
setUpTest();
}
protected abstract void setUpTest();
/**
* Asserts that an error message is present.
* @param model The model which is used to store the error message.
* @param messageCode The message code of the expected error message.
*/
protected void assertErrorMessage(RedirectAttributes model, String messageCode) {
assertFlashMessages(model, messageCode, FLASH_ERROR_MESSAGE);
}
/**
* Asserts that a feedback message is present.
* @param model The model which is used to store the feedback message.
* @param messageCode
*/
protected void assertFeedbackMessage(RedirectAttributes model, String messageCode) {
assertFlashMessages(model, messageCode, FLASH_FEEDBACK_MESSAGE);
}
private void assertFlashMessages(RedirectAttributes model, String messageCode, String flashMessageParameterName) {
Map<String, ?> flashMessages = model.getFlashAttributes();
Object message = flashMessages.get(flashMessageParameterName);
assertNotNull(message);
flashMessages.remove(message);
assertTrue(flashMessages.isEmpty());
verify(messageSourceMock, times(1)).getMessage(eq(messageCode), any(Object[].class), any(Locale.class));
verifyNoMoreInteractions(messageSourceMock);
}
/**
* Asserts that the binding result contains specified field errors.
* @param result The binding result
* @param fieldNames The names which should have validation errors.
*/
protected void assertFieldErrors(BindingResult result, String... fieldNames) {
assertEquals(fieldNames.length, result.getFieldErrorCount());
for (String fieldName : fieldNames) {
assertNotNull(result.getFieldError(fieldName));
}
}
/**
* Binds and validates the given form object.
* @param request The http servlet request object.
* @param formObject A form object.
* @return A binding result containing the outcome of binding and validation.
*/
protected BindingResult bindAndValidate(HttpServletRequest request, Object formObject) {
WebDataBinder binder = new WebDataBinder(formObject);
binder.setValidator(validator);
binder.bind(new MutablePropertyValues(request.getParameterMap()));
binder.getValidator().validate(binder.getTarget(), binder.getBindingResult());
return binder.getBindingResult();
}
/**
* Creates an expected redirect view path.
* @param path The path to the requested view.
* @return The expected redirect view path.
*/
protected String createExpectedRedirectViewPath(String path) {
StringBuilder builder = new StringBuilder();
builder.append(VIEW_REDIRECT_PREFIX);
builder.append(path);
return builder.toString();
}
/**
* Initializes the message source mock to return an error message when
* the error message code given as a a parameter is used to get message
* from message source.
* @param errorMessageCode The wanted error message code.
*/
protected void initMessageSourceForErrorMessage(String errorMessageCode) {
when(messageSourceMock.getMessage(eq(errorMessageCode), any(Object[].class), any(Locale.class))).thenReturn(ERROR_MESSAGE);
}
/**
* Initializes the message source mock to return a feedback message when
* the feedback message code given as a parameter is used to get message
* from message source.
* @param feedbackMessageCode The wanted feedback message code.
*/
protected void initMessageSourceForFeedbackMessage(String feedbackMessageCode) {
when(messageSourceMock.getMessage(eq(feedbackMessageCode), any(Object[].class), any(Locale.class))).thenReturn(FEEDBACK_MESSAGE);
}
/**
* Returns the message source mock.
* @return
*/
protected MessageSource getMessageSourceMock() {
return messageSourceMock;
}
}