如何使用 jmockit 和 spring 的 mockmvc 来测试控制器
how to use jmockit with spring's mockmvc to test controller
我想用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
在设置方法中为空。而且,如果我只是在 @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 的少数 init 方法?有可能吗?
The problem is jmockit can't do well with 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());
}
}
希望对您有所帮助--祝您好运!
与 Mockito 的 @InjectMocks
不同,JMockit 的 @Tested
字段仅在 在 执行任何 @Before
方法后创建。发生这种情况是因为在测试方法中支持模拟参数,这在 Mockito 中不存在。可以说,测试字段应该与模拟字段一起尽早设置,因此这可能会在 JMockit 的未来版本中改变。
无论如何,目前的问题解决方案是:
- 不要使用
@Tested
;相反,在 @Before
方法中手动实例化并注入被测对象。
- 使用
@Tested
,但避免使用依赖于测试字段的@Before
方法。在示例测试中,可以通过调用 MockMvc mockMvc() { return MockMvcBuilders... }
方法在每个测试方法中创建 MockMvc
对象。
最近遇到了类似的问题,找到了一点优雅的解决方法:
@Tested(availableDuringSetup=true)
NotificationController notificationController;
@Injectable
NotificationService notificationService;
private MockMvc mockMvc;
@Before
public void init() {
this.mockMvc = MockMvcBuilders.standaloneSetup(notificationController).build();
}
boolean availableDuringSetup
@Tested
注释的属性是解决方案:)
希望对您有所帮助,
我想用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
在设置方法中为空。而且,如果我只是在 @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 的少数 init 方法?有可能吗?
The problem is jmockit can't do well with 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());
}
}
希望对您有所帮助--祝您好运!
与 Mockito 的 @InjectMocks
不同,JMockit 的 @Tested
字段仅在 在 执行任何 @Before
方法后创建。发生这种情况是因为在测试方法中支持模拟参数,这在 Mockito 中不存在。可以说,测试字段应该与模拟字段一起尽早设置,因此这可能会在 JMockit 的未来版本中改变。
无论如何,目前的问题解决方案是:
- 不要使用
@Tested
;相反,在@Before
方法中手动实例化并注入被测对象。 - 使用
@Tested
,但避免使用依赖于测试字段的@Before
方法。在示例测试中,可以通过调用MockMvc mockMvc() { return MockMvcBuilders... }
方法在每个测试方法中创建MockMvc
对象。
最近遇到了类似的问题,找到了一点优雅的解决方法:
@Tested(availableDuringSetup=true)
NotificationController notificationController;
@Injectable
NotificationService notificationService;
private MockMvc mockMvc;
@Before
public void init() {
this.mockMvc = MockMvcBuilders.standaloneSetup(notificationController).build();
}
boolean availableDuringSetup
@Tested
注释的属性是解决方案:)
希望对您有所帮助,