使用事务模板集成测试手动事务
Integration test a manual transaction with transaction template
当我尝试测试使用手动事务的方法时,我的事务模板出现空指针异常。当我 运行 Spring 中的应用程序启动时,它按预期工作。
@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)
调试此配置时,模板实际上已注入并且不再为空。但是由于我有兴趣测试交易中的操作,我不想模拟它所以我使用:
when(transactionTemplate.execute(Mockito.any())).thenCallRealMethod();
这将引发一个新的空指针异常,因为事务模板尝试使用 TransactionManager 并且它仍然是空的。
如何在事务模板中对我的方法调用进行单元测试?
如果 TransactionManager
是 null
这意味着 Spring 可能没有在测试上下文中加载所有必要的依赖项。
无论如何,如果您需要调用 execute()
方法,为什么还要模拟 TransactionTemplate
?
您的测试看起来像 integration/sociable 测试而不是单元测试。
如果是这样,你不需要模拟任何东西。
如果你想编写一个单元测试来测试 addToA()
方法中的实际逻辑,你应该使用 mock 而不是部分模拟。
供应商中使用的模拟依赖项提供并断言返回了预期的 MyResultEntity
实例。
请注意,您的单元测试将具有有限的价值,并且可能被认为是脆弱的,因为它仅断言调用了一系列方法。通常,您希望根据更具体的逻辑(例如 computation/extraction/transformation)来断言行为。
这是一个例子(没有测试,但它应该在途中给出一个想法):
@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());
}
我通常做的不是调用真实的方法,而是模拟真实的行为。在 mock 中调用真正的方法将失败,因为 mock 不是在 springs 注入上下文中管理的。好吧,确切地说,您可以通过将它们添加到测试配置(普通 SpringMVC)或使用 @MockBean(spring 引导)使它们存在于注入上下文中。但它们仍然只是作为依赖注入。但不会收到任何依赖项。对于单元测试,这通常是所需的行为。
所以只需执行以下操作:
when(_transactionTemplate.execute(any())).thenAnswer(invocation -> invocation.<TransactionCallback<Boolean>>getArgument(0).doInTransaction(_transactionStatus));
_transactionStatus 可以是一个 mock 本身来测试回调中状态的使用。
mocking 是 mock 的用途:)
当我尝试测试使用手动事务的方法时,我的事务模板出现空指针异常。当我 运行 Spring 中的应用程序启动时,它按预期工作。
@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)
调试此配置时,模板实际上已注入并且不再为空。但是由于我有兴趣测试交易中的操作,我不想模拟它所以我使用:
when(transactionTemplate.execute(Mockito.any())).thenCallRealMethod();
这将引发一个新的空指针异常,因为事务模板尝试使用 TransactionManager 并且它仍然是空的。
如何在事务模板中对我的方法调用进行单元测试?
如果 TransactionManager
是 null
这意味着 Spring 可能没有在测试上下文中加载所有必要的依赖项。
无论如何,如果您需要调用 execute()
方法,为什么还要模拟 TransactionTemplate
?
您的测试看起来像 integration/sociable 测试而不是单元测试。
如果是这样,你不需要模拟任何东西。
如果你想编写一个单元测试来测试 addToA()
方法中的实际逻辑,你应该使用 mock 而不是部分模拟。
供应商中使用的模拟依赖项提供并断言返回了预期的 MyResultEntity
实例。
请注意,您的单元测试将具有有限的价值,并且可能被认为是脆弱的,因为它仅断言调用了一系列方法。通常,您希望根据更具体的逻辑(例如 computation/extraction/transformation)来断言行为。
这是一个例子(没有测试,但它应该在途中给出一个想法):
@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());
}
我通常做的不是调用真实的方法,而是模拟真实的行为。在 mock 中调用真正的方法将失败,因为 mock 不是在 springs 注入上下文中管理的。好吧,确切地说,您可以通过将它们添加到测试配置(普通 SpringMVC)或使用 @MockBean(spring 引导)使它们存在于注入上下文中。但它们仍然只是作为依赖注入。但不会收到任何依赖项。对于单元测试,这通常是所需的行为。
所以只需执行以下操作:
when(_transactionTemplate.execute(any())).thenAnswer(invocation -> invocation.<TransactionCallback<Boolean>>getArgument(0).doInTransaction(_transactionStatus));
_transactionStatus 可以是一个 mock 本身来测试回调中状态的使用。
mocking 是 mock 的用途:)