Question

I am trying to test the following controller method using the Spring MVC test API:

@RequestMapping(value = "/preference/email", method = RequestMethod.GET, produces = "text/html")
public String emailForm(@ModelAttribute EmailInfo emailInfo, Model model, @CurrentMember Member member) {
    emailInfo.setEmail(member.getEmail());
    emailInfo.setActivated(member.isActivated());
    emailInfo.setToken(member.getToken());
    model.addAttribute("emailInfo", emailInfo);
    return "preference";
}

When I debug the following test method...

@Test
    public void shouldPopulateEmailInfo() throws Exception {
        when(currentMemberHandlerMethodArgumentResolver.supportsParameter(any(MethodParameter.class))).thenReturn(Boolean.TRUE);
        when(currentMemberHandlerMethodArgumentResolver.resolveArgument(any(MethodParameter.class), any(ModelAndViewContainer.class), any(NativeWebRequest.class), any(WebDataBinderFactory.class))).thenReturn(currentMember);
        mockMvc.perform(get("/preference/email"))//
                .andDo(print())//
                .andExpect(model().attribute("emailInfo.email", "currentMember@example.com"));//
    }

... I do see that "currentMember@example.com" is set on the email field of emailInfo.

However, I systematically get:

java.lang.AssertionError: Model attribute 'emailInfo.email' expected:<currentMember@example.com> but was:<null>
    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:60)
    at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:89)
    at org.springframework.test.web.servlet.result.ModelResultMatchers$2.match(ModelResultMatchers.java:68)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:141)
    at com.bignibou.tests.controller.preference.PreferenceControllerEmailManagementTest.shouldPopulateEmailInfo(PreferenceControllerEmailManagementTest.java:109)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

There is something I don't get with the Spring Mvc Test API.... Can anyone please help?

edit: Here is what I have come up with after taking into account the provided answer:

.andExpect(model().attribute("emailInfo", Matchers.hasProperty("email", Matchers.equalTo("currentMember@example.com"))));//

It works fine now.

Was it helpful?

Solution

As Sotirios pointed out, model().attribute("attributeName") wont allow you to navigate and it will use the whole thing as a name.

However you can use this method instead:

public <T> ResultMatcher attribute(final String name, final Matcher<T> matcher)

This will allow you to specify a Hamcrest matcher to match agains your attributes like:

.andExpect(model().attribute("emailInfo", hasProperty("email", is("currentMember@example.com"))))

Just make sure you have the Hamcrest library in your classpath and import it like:

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*

Note: To import is specially important if you are using Groovy for your tests (Spock) as the method names in Hamcrest conflict with the Groovy standard library

OTHER TIPS

Model attributes are String-key Object-value pairs. There is no expression resolving going on. When you do

model().attribute("emailInfo.email", "currentMember@example.com"));//

You are asking if there is a Model attribute with key emailInfo.email that has the specified value. The answer is obviously no since you don't have a Model attribute with such a key.

What you have is

model.addAttribute("emailInfo", emailInfo);

which is a Model attribute with key emailInfo that holds an object of type EmailInfo. You'll need to compare the email field of the EmailInfo object yourself.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top