gpt4 book ai didi

java - 在@Before 之外使用@InjectMocks

转载 作者:行者123 更新时间:2023-11-30 10:05:51 25 4
gpt4 key购买 nike

我正在为我的单元测试项目(Spring boot rest controller)创建基础,我在传递@InjectMocks 值时遇到问题,因为它仅在@Before 中评估,因此当我尝试访问它时会抛出空指针外面

请提供一些解决问题的技巧?
非常感谢

Ps:关于最佳实践的任何其他建议或我在单元测试中对我当前的基类测试做错的事情也将不胜感激

要测试的类(休息 Controller )

@RestController
@RequestMapping("/management")
@Api(description = "Users count connections", produces = "application/json", tags = {"ConnectionManagement API"})
public class ConnectionManagementControllerImpl implements ConnectionManagementController {

@Autowired
private ConnectionManagementBusinessService connectionManagementBusinessService;

@Override
@PostMapping(value = "/countConnectionsByInterval" , consumes = MediaType.TEXT_PLAIN_VALUE , produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ApiOperation(value = "count all users connections by interval")
public ResponseEntity<List<ConnectionsCountDto>> countConnectionsByInterval(@RequestBody String format) {
List<ConnectionsCountDto> connectionManagement = connectionManagementBusinessService.countConnectionsByInterval(format);
return new ResponseEntity<List<ConnectionsCountDto>>(connectionManagement, HttpStatus.OK);
}

抽象基础测试

public abstract class AbstractBaseTest<C> {

public MockMvc mockMvc;

private Class<C> clazz;

private Object inject;

protected abstract String getURL();

protected final void setTestClass(final Class<C> classToSet, final Object injectToSet) {
clazz = Preconditions.checkNotNull(classToSet);
inject = Preconditions.checkNotNull(injectToSet);
}

@Before
public void init() throws Exception {
MockitoAnnotations.initMocks(clazz);
mockMvc = MockMvcBuilders.standaloneSetup(inject).build();
}

protected MockHttpServletResponse getResponse(MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
get(getURL()).
accept(produces)).
andReturn().
getResponse();
return response;
}

protected MockHttpServletResponse postResponse(String content , MediaType consumes , MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
post(getURL()).
content(content).
contentType(consumes).
accept(produces)).
andReturn().
getResponse();
return response;
}
}

测试类

@RunWith(MockitoJUnitRunner.class)
public class ConnectionManagementControllerImplTest extends AbstractBaseTest<ConnectionManagementControllerImpl>{

@Mock
private ConnectionManagementBusinessService connectionManagementBusinessServiceMocked;

@InjectMocks
private ConnectionManagementControllerImpl connectionManagementControllerMocked;

public ConnectionManagementControllerImplTest() {
super();
setTestClass(ConnectionManagementControllerImpl.class , connectionManagementControllerMocked); // null pointer there
}

@Test
public void countConnectionsByInterval() throws Exception {

// given
given(connectionManagementBusinessServiceMocked.countConnectionsByInterval(Mockito.anyString()))
.willReturn(new ArrayList<ConnectionsCountDto>());

// when
MockHttpServletResponse response = postResponse("day" , MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON_UTF8);

// then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}

@Override
protected String getURL() {
return "/management/countConnectionsByInterval";
}

最佳答案

这按预期工作。但是,您可以手动设置模拟并将它们注入(inject) ConnectionManagementControllerImplTest 构造函数(在调用 setTestClass(...) 之前):

public ConnectionManagementControllerImplTest() {
super();

connectionManagementBusinessServiceMocked = Mockito.mock(ConnectionManagementBusinessService.class);

connectionManagementControllerMocked = new ConnectionManagementControllerImpl();
connectionManagementControllerMocked.setConnectionManagementBusinessService(connectionManagementBusinessServiceMocked);

setTestClass(ConnectionManagementControllerImpl.class, connectionManagementControllerMocked);
}

不要忘记删除 @Mock@InjectMocks 注释。顺便说一句,在这种情况下,您甚至可以删除 @RunWith(MockitoJUnitRunner.class)

更新: 测试类的构造函数和用 @Before 注释的“init”方法都会为每个测试执行。不同之处在于 Mockito 注释是在构造函数和 @Before 方法调用之间处理的。

因此您可以稍微更改您的代码以获得积极的结果:

  1. ConnectionManagementControllerImplTest 中创建“init”方法(使用 @Before 注释)并将 setTestClass() 从构造函数中移入其中(在那个特殊情况下,您还可以删除整个构造函数,因为它只包含 super() 调用)。
  2. setTestClass() 行之后添加 super.init()(否则 JUnit 将忽略父类中的“init”方法)。
  3. (可选)如果您的测试是以相同的方式编写的,您还可以从父类的“init”方法中删除 @Before 注释。

以这种方式重构的代码示例:

public abstract class AbstractBaseTest<C> {

public MockMvc mockMvc;

private Class<C> clazz;

private Object inject;

protected abstract String getURL();

protected final void setTestClass(final Class<C> classToSet, final Object injectToSet) {
clazz = Preconditions.checkNotNull(classToSet);
inject = Preconditions.checkNotNull(injectToSet);
}

@Before //this annotation can be removed
public void init() throws Exception {
MockitoAnnotations.initMocks(clazz); //this line also can be removed because MockitoJUnitRunner does it for you
mockMvc = MockMvcBuilders.standaloneSetup(inject).build();
}

protected MockHttpServletResponse getResponse(MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
get(getURL()).
accept(produces)).
andReturn().
getResponse();
return response;
}

protected MockHttpServletResponse postResponse(String content , MediaType consumes , MediaType produces) throws Exception {
MockHttpServletResponse response = mockMvc.perform(
post(getURL()).
content(content).
contentType(consumes).
accept(produces)).
andReturn().
getResponse();
return response;
}
}
@RunWith(MockitoJUnitRunner.class)
public class ConnectionManagementControllerImplTest extends AbstractBaseTest<ConnectionManagementControllerImpl> {

@Mock
private ConnectionManagementBusinessService connectionManagementBusinessServiceMocked;

@InjectMocks
private ConnectionManagementControllerImpl connectionManagementControllerMocked;

//constructor can be removed
public ConnectionManagementControllerImplTest() {
super();
}

@Before
public void init() throws Exception {
setTestClass(ConnectionManagementControllerImpl.class, connectionManagementControllerMocked);
super.init();
}

@Test
public void countConnectionsByInterval() throws Exception {

// given
given(connectionManagementBusinessServiceMocked.countConnectionsByInterval(Mockito.anyString()))
.willReturn(new ArrayList<ConnectionsCountDto>());

// when
MockHttpServletResponse response = postResponse("day", MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON_UTF8);

// then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}

@Override
protected String getURL() {
return "/management/countConnectionsByInterval";
}
}

附言我更喜欢前一种方法,但如果您不想为 ConnectionManagementBusinessService 使用 setter,则可以选择后者。我已经测试了它们,结果是一样的。

关于java - 在@Before 之外使用@InjectMocks,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55007125/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com