【问题标题】:how to use jmockit with spring's mockmvc to test controller如何使用 jmockit 和 spring 的 mockmvc 来测试控制器
【发布时间】:2015-01-06 02:02:43
【问题描述】:

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

问题是 jmockit 不能很好地处理mockmvc,无论是standaloneSetup() 还是webAppContextSetup()

另一个名为 Mockito 的模拟工具很好地解决了这个问题,但它在模拟依赖项方面有很多限制。

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

示例代码如下:

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

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 方法中为空。而且,如果我只是在@Tested 方法中调用loginController.xxx() 就可以了。我认为这表明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 的少数初始化方法?有可能吗?

【问题讨论】:

  • 你能澄清一下吗?我不知道您所说的独立/webappcontext 设置是什么意思。使用 mockmvc 的示例测试类会有所帮助。
  • @Rogério,你注意到我的问题真是太棒了。很抱歉这么久的更新。顺便说一句,jmockit 真的很强大。
  • 这不太符合 JMockit 的精神,但您始终可以在 @Before 方法中自己实例化 loginController
  • @dcsohl,自己实例化 loginController 很简单。但是,如果我这样做,则无法自动注入依赖对象。这也是困扰我的。

标签: spring unit-testing jmockit


【解决方案1】:

与 Mockito 的 @InjectMocks 不同,JMockit 的 @Tested 字段仅在执行任何 @Before 方法之后创建。发生这种情况是因为在测试方法中支持模拟参数,而 Mockito 中不存在模拟参数。可以说,应该尽早设置测试字段以及模拟字段,因此这可能会在 JMockit 的未来版本中发生变化。

无论如何,目前的问题的解决方案是:

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

【讨论】:

  • 感谢您的解决方案。我认为第二种方式更能满足我的要求。 Before 方法实际上是在 junit 中的每个 Test 方法之前执行的,所以使用第二种方法,唯一的区别是代码不太干净。
【解决方案2】:

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

@Tested(availableDuringSetup=true)
NotificationController notificationController;

@Injectable
NotificationService notificationService;

private MockMvc mockMvc;

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

@Tested 注释的boolean availableDuringSetup 属性是解决方案:)

希望对你有帮助,

【讨论】:

    【解决方案3】:

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

    希望它能帮到你——祝你好运!

    【讨论】:

    • 你是对的。谢谢你的提醒。这是集成测试的好方法,但是我必须先解决单元测试的问题。
    • 是的,我发现我共享的代码相当丑陋,但我正在寻找一些没有受益于 TDD 的人编写的代码,所以我对集成测试感到满意。很高兴 Rogerio 能够帮助您!
    猜你喜欢
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    • 2018-05-25
    • 1970-01-01
    • 2021-03-14
    • 2021-07-24
    • 2013-01-11
    • 2018-11-23
    相关资源
    最近更新 更多