Spring 引导 @WebMvcTest 使 @MockBean 为 null - 使用 SpringRunner 工作
Spring Boot @WebMvcTest having @MockBean null - using SpringRunner works
短:
从 Junit4 迁移到 JUnit5 有困难。删除 @RunWith(SpringRunner.class)
后,我的 @MockBean
注释服务为空,尽管我使用的是“@WebMvcTest”。为什么它不起作用?
长:
几天以来我一直在努力解决这个问题。我正在对我的 Spring 引导应用程序进行一些测试,该应用程序使用 @RunWith(SpringRunner.class)
im 与 @WebMvcTest
.
的组合运行良好
因为那是 JUnit4,所以我试图迁移到 Junit5。我从 pom 中删除了 vintage 依赖项并添加了 spring-boot-starter-test
的排除项。 JUnit 5 只剩下 junit-jupiter
依赖项。
我阅读了一些相关的帖子和样本。因为我不想加载完整的上下文,所以 SpringBootTest 不是这样(而且根本不起作用)
我 'think' 我发现 @WebMvcTest
就足够了,因为它已经包含 @extendWith
注释,这是 @RunWith(SpringRunner.class)
的 JUnit5 等价物。
所以:我只需要删除 springRunner 就可以了。
但是删除那一行之后,我的带有@MockBean 注释的服务从现在开始为空。
我可以使用 @WebMvcTest
和 @MockBean
找到很多示例。所以我不明白,为什么我的不工作。
添加 @RunWith(SpringRunner.class)
使其再次工作。
这就是测试 class(没有 springrunner):
package my.package;
import com.fasterxml.jackson.databind.ObjectMapper;
import my.package.controller.SkillTileCommand;
import my.package.controller.SkillTileController;
import my.package.controller.SkillTileResponse;
import my.package.service.SkillTileService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doNothing;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(SkillTileController.class)
public class SkillTileControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@MockBean
private SkillTileService skillTileService;
private final SkillTileTestDataFactory testDataFactory = new SkillTileTestDataFactory();
@Test
public void givenSkillTiles_whenGETFindAll_thenReturnJsonArray() throws Exception {
List<SkillTileResponse> skillTiles = testDataFactory.createTestSkillTileResponses(1, true, null, true);
SkillTileResponse st1 = skillTiles.get(0);
given(skillTileService.findAll(null)).willReturn(skillTiles);
mvc.perform(get("/skill-tile/")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].name").value(st1.getName()));
}
}
而不是使用 Spring 转轮是这样的:
package my.package;
import com.fasterxml.jackson.databind.ObjectMapper;
import my.package.controller.SkillTileCommand;
import my.package.controller.SkillTileController;
import my.package.controller.SkillTileResponse;
import my.package.service.SkillTileService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doNothing;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@WebMvcTest(SkillTileController.class)
public class SkillTileControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@MockBean
private SkillTileService skillTileService;
[...]
}
可能服务负责人也有兴趣:
package my.package.service;
import lombok.RequiredArgsConstructor;
import my.package.controller.SkillTileCommand;
import my.package.controller.SkillTileResponse;
import my.package.exception.ResourceNotFoundException;
import my.package.repository.SkillTileEntity;
import my.package.repository.SkillTileRepository;
import my.package.utils.ObjectMapper;
import my.package.utils.ResourceUtility;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
@Service
public class SkillTileService {
private final SkillTileRepository skillTileRepository;
[...]
}
以下是与测试相关的两个依赖项:
[...]
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
[...]
谢谢你的帮助。
最好不要只留在 JUnit4。
编辑:
我也有与另一个 testclass 相同的行为,它仅用 @DataJpaTest
注释并具有 @Autowire
字段。他们的 null 没有 @RunWith
您有可能在您的控制器中使用 @RequiredArgsConstructor
。
确保您已将服务字段指定为 final
。
@RequiredArgsConstructor
文档声明将使用未初始化的 final 或 @NonNull 字段的参数创建构造函数。如果 SkillTileService 依赖项未标记为 final
,则不会将其添加为构造函数参数。
因此,虽然 mock 本身不为空,但它仍然可能永远不会注入到被测试的控制器中。
如果您仔细查看您的测试,您会发现您的 @Test
注释仍然来自旧的 org.junit
包。这是 JUnit4。这意味着您的测试实际上是 运行 使用 JUnit4 而不是 JUnit5。这基本上忽略了所有注释,因此也忽略了 @WebMvcTest
.
要修复,请确保您的 @Test
注释也来自正确的 org.junit.jupiter.api
包,以使其成为正确的 JUnit5 测试。
短:
从 Junit4 迁移到 JUnit5 有困难。删除 @RunWith(SpringRunner.class)
后,我的 @MockBean
注释服务为空,尽管我使用的是“@WebMvcTest”。为什么它不起作用?
长:
几天以来我一直在努力解决这个问题。我正在对我的 Spring 引导应用程序进行一些测试,该应用程序使用 @RunWith(SpringRunner.class)
im 与 @WebMvcTest
.
因为那是 JUnit4,所以我试图迁移到 Junit5。我从 pom 中删除了 vintage 依赖项并添加了 spring-boot-starter-test
的排除项。 JUnit 5 只剩下 junit-jupiter
依赖项。
我阅读了一些相关的帖子和样本。因为我不想加载完整的上下文,所以 SpringBootTest 不是这样(而且根本不起作用)
我 'think' 我发现 @WebMvcTest
就足够了,因为它已经包含 @extendWith
注释,这是 @RunWith(SpringRunner.class)
的 JUnit5 等价物。
所以:我只需要删除 springRunner 就可以了。
但是删除那一行之后,我的带有@MockBean 注释的服务从现在开始为空。
我可以使用 @WebMvcTest
和 @MockBean
找到很多示例。所以我不明白,为什么我的不工作。
添加 @RunWith(SpringRunner.class)
使其再次工作。
这就是测试 class(没有 springrunner):
package my.package;
import com.fasterxml.jackson.databind.ObjectMapper;
import my.package.controller.SkillTileCommand;
import my.package.controller.SkillTileController;
import my.package.controller.SkillTileResponse;
import my.package.service.SkillTileService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doNothing;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(SkillTileController.class)
public class SkillTileControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@MockBean
private SkillTileService skillTileService;
private final SkillTileTestDataFactory testDataFactory = new SkillTileTestDataFactory();
@Test
public void givenSkillTiles_whenGETFindAll_thenReturnJsonArray() throws Exception {
List<SkillTileResponse> skillTiles = testDataFactory.createTestSkillTileResponses(1, true, null, true);
SkillTileResponse st1 = skillTiles.get(0);
given(skillTileService.findAll(null)).willReturn(skillTiles);
mvc.perform(get("/skill-tile/")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].name").value(st1.getName()));
}
}
而不是使用 Spring 转轮是这样的:
package my.package;
import com.fasterxml.jackson.databind.ObjectMapper;
import my.package.controller.SkillTileCommand;
import my.package.controller.SkillTileController;
import my.package.controller.SkillTileResponse;
import my.package.service.SkillTileService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doNothing;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringRunner.class)
@WebMvcTest(SkillTileController.class)
public class SkillTileControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@MockBean
private SkillTileService skillTileService;
[...]
}
可能服务负责人也有兴趣:
package my.package.service;
import lombok.RequiredArgsConstructor;
import my.package.controller.SkillTileCommand;
import my.package.controller.SkillTileResponse;
import my.package.exception.ResourceNotFoundException;
import my.package.repository.SkillTileEntity;
import my.package.repository.SkillTileRepository;
import my.package.utils.ObjectMapper;
import my.package.utils.ResourceUtility;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
@Service
public class SkillTileService {
private final SkillTileRepository skillTileRepository;
[...]
}
以下是与测试相关的两个依赖项:
[...]
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
[...]
谢谢你的帮助。 最好不要只留在 JUnit4。
编辑:
我也有与另一个 testclass 相同的行为,它仅用 @DataJpaTest
注释并具有 @Autowire
字段。他们的 null 没有 @RunWith
您有可能在您的控制器中使用 @RequiredArgsConstructor
。
确保您已将服务字段指定为 final
。
@RequiredArgsConstructor
文档声明将使用未初始化的 final 或 @NonNull 字段的参数创建构造函数。如果 SkillTileService 依赖项未标记为 final
,则不会将其添加为构造函数参数。
因此,虽然 mock 本身不为空,但它仍然可能永远不会注入到被测试的控制器中。
如果您仔细查看您的测试,您会发现您的 @Test
注释仍然来自旧的 org.junit
包。这是 JUnit4。这意味着您的测试实际上是 运行 使用 JUnit4 而不是 JUnit5。这基本上忽略了所有注释,因此也忽略了 @WebMvcTest
.
要修复,请确保您的 @Test
注释也来自正确的 org.junit.jupiter.api
包,以使其成为正确的 JUnit5 测试。