未被mockito模拟的自动连接Bean上的指针为空

cuxqih21  于 2022-11-08  发布在  其他
关注(0)|答案(7)|浏览(221)

我有一个服务类,我需要对它进行单元测试。这个服务有一个upload方法,它会调用其他更新数据库的服务(autowired bean)。我需要模拟其中的一些服务,还有一些服务可以按原样执行。

  1. @Service
  2. public class UploadServiceImpl implements UploadService{
  3. @Autowired
  4. private ServiceA serviceA;
  5. @Autowired
  6. private ServiceB serviceB;
  7. public void upload(){
  8. serviceA.execute();
  9. serviceB.execute():
  10. //code...
  11. }

在上面的例子中,我需要模拟ServiceA,但是我希望ServiceB按原样运行并执行它的功能。我的Junit测试如下所示:

  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @SpringBootTest(classes=Swagger2SpringBoot.class)
  3. public class UploadServiceTest {
  4. @Mock
  5. private ServiceA serviceA;
  6. @InjectMocks
  7. private UploadServiceImpl uploadService;
  8. @Before
  9. public void init() {
  10. MockitoAnnotations.initMocks(this);
  11. }
  12. @Test
  13. public void testUpload(){
  14. uploadService.upload();
  15. }

当我执行这个命令时,我在UploadServiceImpl中得到serviceB.execute();处的NPE。
有什么问题吗?

**注意:**我没有指定被模拟对象的行为,因为我并不真正关心,而且被模拟对象的默认行为是什么也不做。

谢谢你!

f8rj6qna

f8rj6qna1#

通常在单元测试时,你想要模拟一个类的所有外部依赖项,这样单元测试就可以保持独立,并且专注于被测试的类。
然而,如果您想混合Spring自动化和Mockito模拟,一个简单的解决方案是同时使用@InjectMocks@Autowired进行注解:

  1. @InjectMocks
  2. @Autowired
  3. private UploadServiceImpl uploadService;

这样做的实际效果是,首先Spring将自动连接bean,然后Mockito将立即用可用的模拟覆盖模拟的依赖项。

t5fffqht

t5fffqht2#

您面临的问题是由于使用了@InjectMocks注解。@InjectMocks标记了应执行注入的字段。Mockito将尝试仅通过构造函数注入、setter注入或属性注入来注入模拟-按此顺序。如果任何给定的注入策略失败,Mockito将不会报告失败。
因此,在您的情况下,当尝试注入模拟时,只有一个模拟bean存在,而另一个beanServiceA没有被注入。
您可以尝试完全不使用@InjectMocks,而是为您要模拟的方法传递一个模拟对象,同时将其余自动连接的对象传递到构造函数中。示例:
在这里,为了测试,我传递了一个模拟对象和一个自动连接对象。

  1. @RunWith(MockitoJUnitRunner.class)
  2. public class SampleTestServiceImplTest {
  3. @Mock
  4. private SampleClient sampleClient;
  5. @Autowired
  6. private BackendService backendService ;
  7. private BackendServiceImpl backendServiceimpl;
  8. @Before
  9. void setUp() {
  10. backendServiceimpl = new BackendServiceImpl(sampleClient, backendService);
  11. }

或者,另一种方法是将@Autowired注解与@InjectMocks.@Autowired @InjectMocks沿着使用,它将注入模拟的类,而自动连接的注解添加该类可能具有的任何其他依赖项。
答案来自:https://medium.com/@vatsalsinghal/autowired-and-injectmocks-in-tandem-a424517fdd29

展开查看全部
x6492ojm

x6492ojm3#

新增

  1. @Mock
  2. private ServiceB serviceB;

以创建可注入缺失服务的模拟,就像对服务A所做的那样。

wd2eg0qa

wd2eg0qa4#

在我的例子中,除了使用@InjectMocks和@Autowired的组合之外,我还必须为被测试类中的模拟对象提供setter(在原始示例中,为UploadServiceImpl中的ServiceA提供setter)。

gorkyyrv

gorkyyrv5#

另一种方法是定义一个自动连接的构造函数,以便您可以正确地测试服务。

  1. @Service
  2. public class UploadServiceImpl implements UploadService{
  3. private ServiceA serviceA;
  4. private ServiceB serviceB;
  5. @Autowired
  6. public UploadServiceImpl(ServiceA serviceA, ServiceB serviceB) {
  7. this.serviceA = serviceA;
  8. this.serviceB = serviceB;
  9. }
  10. public void upload(){
  11. serviceA.execute();
  12. serviceB.execute():
  13. //code...
  14. }
展开查看全部
qncylg1j

qncylg1j6#

在我看来,我们正在编写单元测试用例,我们不应该为了测试一段代码而初始化Spring上下文。
所以,
我使用Mockito来模拟我的主目标测试类中的Autowired bean,并将这些模拟bean注入到我的主测试类Object中
可能听起来令人困惑,请参见以下示例💥
我使用的依赖项

  1. testImplementation("org.mockito:mockito-core:2.28.2")
  2. testImplementation("org.mockito:mockito-inline:2.13.0")
  3. testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
  4. testImplementation("org.mockito:mockito-junit-jupiter:4.0.0")

我的主要类是数学和计算器bean是自动连接的

  1. class Maths{
  2. @Autowired Calculator cal;
  3. .........
  4. .........
  5. public void randomAddMethod(){
  6. cal.addTwoNumbers(1,2); // will return 3;
  7. }
  8. }

测试类

  1. @ExtendWith(MockitoExtension.class)
  2. class MathsTest{
  3. @Mock(answer = Answers.RETURNS_DEEP_STUBS) Calculator cal;
  4. @InjectMocks Maths maths = new Maths();
  5. @Test testMethodToCheckCalObjectIsNotNull(){
  6. maths.randomAddMethod();
  7. }
  8. }

现在,cal在Maths类中将不为空,并将按预期工作

展开查看全部
kg7wmglp

kg7wmglp7#

如果不使用ReflectionTestUtils,我就无法让它工作。如果可行的话,设置构造函数是一个选项。

相关问题