当我尝试测试使用手动事务的方法时,我的事务模板上出现空指针异常。当我在Spring Boot中运行应用程序时,它按预期工作。
@Autowired
TransactionTemplate template;
public CompletableFuture<MyResultEntity> addToA(BInput input) {
return CompletableFuture
.supplyAsync(
() -> template.execute(status -> {
A a = aRepository.findOne(input.getA());
List<B> addedBs = saveBs(input.getB(), a);
return new MyResultEntity(a, addedBs);
}), MyCustomExecutor());
}
我尝试过使用模拟模板,然后像这样注入:
@Mock
private TransactionTemplate transactionTemplate;
@InjectMocks
private MyClass myClass;
我也试过用以下方法注释我的测试:
@RunWith(SpringJUnit4ClassRunner.class)
调试此配置时,模板实际上已注入,不再为null。但由于我有兴趣测试交易中的操作,我不想嘲笑它,所以我使用:
when(transactionTemplate.execute(Mockito.any())).thenCallRealMethod();
这会抛出一个新的空指针异常,因为事务模板试图使用TransactionManager并且仍为null。
如何在事务模板中对方法调用进行单元测试?
答案 0 :(得分:2)
我通常做的不是调用real方法,而是模拟真实行为。在模拟中调用real方法将失败,因为在Spring注入上下文内部未管理模拟。好吧,确切地说,您可以通过将它们添加到测试配置(纯SpringMVC)或使用@MockBean(spring boot),使它们存在于注入上下文中。但是仍然将它们作为依赖项注入。但是不会收到任何依赖。对于单元测试,这通常是期望的行为。
所以要做类似的事情:
import re
s ='417,364.4265,2535.2258,16.7616,143.5451,0,0 ; Leviathan'
re.split(r' *[,;] *',s)
# ['417', '364.4265', '2535.2258', '16.7616', '143.5451', '0', '0', 'Leviathan']
_transactionStatus可以是一个模拟自己,以测试回调中状态的使用。
嘲笑是用于模拟的:)
答案 1 :(得分:1)
如果TransactionManager
是null
,则意味着Spring可能没有在测试环境中加载所有必需的依赖项。
无论如何,如果你需要调用TransactionTemplate
方法,为什么要嘲笑execute()
?
您的测试看起来像集成/社交测试,而不是单元测试
如果是这样的话,你不需要嘲笑任何东西
如果你想编写一个测试addToA()
方法中实际逻辑的单元测试,你应该使用mock而不进行局部模拟。
供应商中使用的模拟依赖关系提供并声明返回预期的MyResultEntity
实例。
请注意,您的单元测试将具有有限的值,并且可能被认为是脆弱的,因为它仅断言调用了一系列方法。通常,您希望根据更具体的逻辑(例如计算/提取/转换)来断言行为。
这是一个例子(没有经过测试,但它应该在路上给出一个想法):
@Mock
Repository ARepositoryMock;
@Mock
Repository BRepositoryMock;
@Test
public void addToA() throws Exception {
BInput input = new BInput();
// record mock behaviors
A aMockByRepository = Mockito.mock(A.class);
List<B> listBMockByRepository = new arrayList<>();
Mockito.when(ARepositoryMock.findOne(input.getA())).thenReturn(aMockByRepository);
Mockito.when(BRepositoryMock.saveBs(input.getB(), aMockByRepository)).thenReturn(listBMockByRepository);
// action
CompletableFuture<MyResultEntity> future = myObjectUnderTest.addToA(input);
//assertion
MyResultEntity actualResultEntity = future.get();
Assert.assertEquals(aMockByRepository, actualResultEntity.getA());
Assert.assertEquals(listBMockByRepository, actualResultEntity.getListOfB());
}