我有一个服务类需要单元测试。该服务有一个上传方法,该方法又调用其他服务(自动连线的 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();
}
当我执行此操作时,我在UploadServiceImpl
的serviceB.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 在我的主目标测试类中模拟Autowired
bean,并将这些模拟 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,我就无法让它工作。如果构造函数对您可行,则设置构造函数是一种选择。