自动连线的 Bean 上的空指针,未被 mockito 模拟



我有一个服务类需要单元测试。该服务有一个上传方法,该方法又调用其他服务(自动连线的 bean(来更新数据库。我需要模拟其中一些服务和一些按原样执行。

@Service
public class UploadServiceImpl implements UploadService{
@Autowired
private ServiceA serviceA;
@Autowired
private ServiceB serviceB;
public void upload(){
serviceA.execute();
serviceB.execute():
//code...
}

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

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringBootTest(classes=Swagger2SpringBoot.class) 
public class UploadServiceTest {
@Mock
private ServiceA serviceA;
@InjectMocks
private UploadServiceImpl uploadService;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testUpload(){
uploadService.upload();
}

当我执行此操作时,我在UploadServiceImplserviceB.execute();得到 NPE。

可能是什么问题?

注意:我没有指定模拟对象的行为,因为我并不真正关心,而且模拟对象的默认行为是不执行任何操作。

谢谢!

通常在单元测试时,您希望模拟类的所有外部依赖项。这样,单元测试可以保持独立并专注于所测试的类。

尽管如此,如果您想将 Spring 自动布线与 Mockito 模拟混合,一个简单的解决方案是同时使用@InjectMocks@Autowired进行注释:

@InjectMocks
@Autowired
private UploadServiceImpl uploadService;

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

您面临的问题是由于使用了@InjectMocks注释。@InjectMocks标记应执行注射的字段。Mockito将尝试仅通过构造函数注入,setter注入或属性注入来注入模拟 - 按此顺序。如果任何给定的注入策略失败,那么 Mockito 不会报告失败。

因此,在您尝试注入模拟的情况下,只有一个模拟 Bean 存在,而另一个 beanServiceA没有被注入。要解决此问题:

您可以尝试根本不使用@InjectMocks,而是为要模拟的方法传递一个模拟对象,同时将其余自动连线的对象传递到构造函数中。例:

在这里测试我正在传递一个模拟对象和一个自动连线对象。

@RunWith(MockitoJUnitRunner.class)
public class SampleTestServiceImplTest {
@Mock
private SampleClient sampleClient;
@Autowired
private BackendService backendService ;
private BackendServiceImpl backendServiceimpl;
@Before
void setUp() {
backendServiceimpl = new BackendServiceImpl(sampleClient, backendService);
}

或者另一种可以完成这项工作的方法是使用@Autowired注释以及一起使用@InjectMocks.@Autowired @InjectMocks,它将要做的是注入模拟类,Autowired 注释添加该类可能具有的任何其他依赖项。

答案来自 : https://medium.com/@vatsalsinghal/autowired-and-injectmocks-in-tandem-a424517fdd29

在我看来,我们正在编写单元测试用例,我们不应该为了测试一段代码而初始化 spring 上下文。

所以

我使用 Mockito 在我的主目标测试类中模拟Autowiredbean,并将这些模拟 bean 注入到我的主测试类 Object 中

可能听起来令人困惑,请参阅以下示例 💥

我使用的依赖项

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

我的主要课程是数学和计算器 bean 是自动连线


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

测试类


@ExtendWith(MockitoExtension.class)
class MathsTest{
@Mock(answer = Answers.RETURNS_DEEP_STUBS) Calculator cal;
@InjectMocks Maths maths = new Maths();
@Test testMethodToCheckCalObjectIsNotNull(){
maths.randomAddMethod();
}
}

现在cal在数学课上不会为空,并且会按预期工作

添加

@Mock
private ServiceB serviceB;

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

就我而言,除了使用 @InjectMocks 和 @Autowired 的组合之外,我还必须在测试类中为模拟对象提供 setter(原始示例中 UploadServiceImpl 中的 ServiceA 的 setter(。没有它,服务A的真正方法就被称为。

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

@Service
public class UploadServiceImpl implements UploadService{
private ServiceA serviceA;
private ServiceB serviceB;
@Autowired
public UploadServiceImpl(ServiceA serviceA, ServiceB serviceB) {
this.serviceA = serviceA;
this.serviceB = serviceB;
}
public void upload(){
serviceA.execute();
serviceB.execute():
//code...
}

如果不使用 ReflectionTestUtils,我就无法让它工作。如果构造函数对您可行,则设置构造函数是一种选择。

最新更新