如何使用jmockit和spring的mockmvc来测试控制器

时间:2015-01-06 02:02:44

标签: spring unit-testing jmockit

我想使用mockmvc来测试Spring推荐的控制器。但是,我还必须使用jmockit来模拟依赖性。

问题在于jmockit与mockmvc无关,无论是standaloneSetup()还是webAppContextSetup()

另一个名为Mockito的模拟工具已经完成了这个问题,但它在模拟依赖项方面有很多限制。

所以,任何人都有经验或想法,请告诉我。非常感谢你。

示例代码如下:

第一个是Mockito和Spring的MockMvc单元测试控制器。运行良好。

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();
    }
}

其次,jmockit如下。不幸的是,loginController在setup方法中为null。而且,如果我只是调用loginController.xxx()方法中的@Tested就可以了。我认为这表明loginController方法在@Tested方法之前但在@Before方法之后被实例化。

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();
    }
}

那么,这个问题怎么解决? jmockit的少数初始化方法?有可能吗?

3 个答案:

答案 0 :(得分:3)

与Mockito的@InjectMocks不同,JMockit的@Tested字段仅在执行任何@Before方法后才会创建。这是因为测试方法中对模拟参数的支持,这在Mockito中并不存在。可以说,测试字段应该与模拟字段一起提前设置,因此这可能会在未来版本的JMockit中发生变化。

无论如何,现在问题的解决方案是:

  1. 不要使用@Tested;相反,在@Before方法中手动实例化并注入被测对象。
  2. 使用@Tested,但要避免依赖于测试字段的@Before方法。在示例测试中,可以通过调用MockMvc方法在每个测试方法中创建MockMvc mockMvc() { return MockMvcBuilders... }对象。

答案 1 :(得分:1)

  

问题是jmockit不能很好地使用mockmvc

我发现JMockit和Spring的MockMvc确实在一起玩得很好。我在我的案例中成功使用了webAppContextSetup。这是一个可能无法编译的示例,但可能是一个有用的指南,可以帮助您入门..

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());
    }

}

希望它可以帮到你 - 祝你好运!

答案 2 :(得分:1)

我最近遇到过类似的问题,我找到了一些优雅的解决方案:

@Tested(availableDuringSetup=true)
NotificationController notificationController;

@Injectable
NotificationService notificationService;

private MockMvc mockMvc;

@Before
public void init() {
    this.mockMvc = MockMvcBuilders.standaloneSetup(notificationController).build();
}
boolean availableDuringSetup注释的

@Tested属性是解决方案:)

希望有所帮助,