How to use jmockit with spring mockmvc to test a controller

I want to use mockmvc

for testing a controller that Spring recommends. But I also have to use jmockit

mock dependencies.

The problem is that jmockit cannot handle mockmvc

, be it standaloneSetup()

or webAppContextSetup()

.

Another mocking tool called Mockito handles this problem well, but it has a lot of limitations in mocking dependencies.

So, does anyone have any experience or idea, please tell me. Thank you very much.

Sample code looks like this:

The first is Mockito with a Spring controller mockmvc

prior to unit test. It works well.

public class TestControllerTest {

    @InjectMocks
    private LoginController loginController;

    @Mock
    private LoginService loginService;

    private MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(loginController).build();
    }

    @Test
    public void testLogin() throws Exception {

        when(loginService.login()).thenReturn(false);

        this.mockMvc.perform(get("/login"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(view().name("goodbyeworld"))
                .andReturn();
    }
}

      

Second, jmockit looks like this. Unfortunately loginController

null in the setup method. And, if I just call loginController.xxx()

in a method @Tested

, that's fine. I think this shows what loginController

is being created before the method @Tested

, but after the @Before

method.

public class TestControllerTest2 {
    @Tested
    private LoginController loginController;

    @Injectable
    private LoginService loginService;

    private MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        this.mockMvc = MockMvcBuilders.standaloneSetup(loginController).build();
    }

    @Test
    public void testLogin() throws Exception {

        new Expectations() {{
            loginService.login(); result = false;
        }};

        this.mockMvc.perform(get("/login"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(view().name("goodbyeworld"))
                .andReturn();
    }
}

      

So how do you fix this problem? jmockit handful init method? any possible?

+3


source to share


3 answers


Unlike Mockito @InjectMocks

, JMockit fields @Tested

are created only after any methods have been executed @Before

. This is due to the support for mock parameters in test methods that Mockito does not have. It is possible that the checked fields should have been set before, along with the false fields, so this may change in a future version of JMockit.

If anything, the solutions to the problem facing today are as follows:



  • Do not use @Tested

    ; instead instantiate and inject the object of interest manually in the method @Before

    .
  • Use @Tested

    but avoid methods @Before

    that depend on the checked fields. In the sample test, an object MockMvc

    can be created in each test method by calling the method MockMvc mockMvc() { return MockMvcBuilders... }

    .
+3


source


Jmockit cannot succeed in mockmvc

I find that JMockit and Spring MockMvc play well together. I have used webAppContextSetup successfully in my case. Here's an example, which may not even compile, but can be a helpful guide to getting started.



import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import mockit.*;

import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;

import some.package.Account;
import some.package.Collaborator;

@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@WebAppConfiguration
@ContextConfiguration(locations = { "classpath:/context/example1.xml", "classpath:/context/example2.xml" })
public class AccountControllerIntegrationTest {
    private static final String PATH_TO_ACCOUNT = "/accounts/some_account";

    private String exampleAccountJson = "{\"account\":\"Sample\",\"active\":true}";

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Mocked
    private Account mockAccount;

    @Mocked
    private Collaborator mockCollaborator;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void should_delete_account() throws Exception {
        new Expectations() {{
            mockAccount.getSomethingWhichReallyShouldNotBeExposed(); result = mockCollaborator;
            mockCollaborator.getSomething(); result = "whatever";
        }};
        mockMvc.perform(delete(PATH_TO_ACCOUNT)).andExpect(status().isOk());
    }

    @Test
    public void should_add_account() throws Exception {
        new NonStrictExpectations() {{
            mockAccount.getSomethingWhichReallyShouldNotBeExposed(); result = mockCollaborator;
            mockCollaborator.getSomething(); result = "whatever";
        }};
        mockMvc.perform(put(PATH_TO_ACCOUNT).contentType(MediaType.APPLICATION_JSON).content(exampleAccountJson)).andExpect(status().isOk());
    }

}

      

Hope this can help you - good luck!

+1


source


I ran into a similar problem lately and I found a slightly neat solution:

@Tested(availableDuringSetup=true)
NotificationController notificationController;

@Injectable
NotificationService notificationService;

private MockMvc mockMvc;

@Before
public void init() {
    this.mockMvc = MockMvcBuilders.standaloneSetup(notificationController).build();
}

      

boolean availableDuringSetup

attribute for @Tested

annotation is the solution :)

Hope it helps,

+1


source







All Articles