在使用@Retention,@Transactional,@Inherited进行注释服务的testing之后,TestNGunit testing不起作用

我正在testing一个商业服务与TestNG,在春季启动应用程序的mockitounit testing。

应用程序是多模块弹簧引导项目。我正在为业务模块编写unit testing。

我已经在pom中添加了以下相关性testing,

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>${testng.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>javax.el</groupId> <artifactId>el-api</artifactId> <version>${javaxel.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.servlet</artifactId> <version>${javax.servlet.version}</version> <scope>test</scope> </dependency> 

我的包装注释看起来像

 @Service @Transactional @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface MyServiceAnnotation{} 

我的TestApp看起来像

 @SpringBootApplication public class TestApp{ .... } 

我的商业服务看起来像

 @MyServiceAnnotation public class AddressServiceImpl implements AddressService { @Autowire UserDAO userDAO; @Autowire AddressDAO addressDAO; public Address find(int userId) { user = userDAO.findOne(userId); /** if I run following test then I get user NULL. But it should get user object which I have created in data provider **/ if(user == null ) { throw new BadReqExcp("invalid user Id", 101); } address = user.findAddresses(); if(address is empty) { throw new BadReqExcp("add not found", 102);} return address; } } 

MyTestClass看起来像

 @ContextConfiguration(classes = { TestApp.class }) class MyTestClass{ @Mock UserDAO userDAO; @InjectMocks @Autowire AddressService addressServie; @BeforeMethod public void initMock() { MockitoAnnotations.initMocks(this); } @Test(dataProvider = "getUser", dataProviderclass = UserDP.class) public void shouldThrowExceptionAddressNotFound(int userId, User user) { when(userDAO.findOne(userId)).thenReturn(user); //here dao call should return user but it is returning null try{ addressService.find(userId); } catch(BadReqExcp e){ // Here errro code should be 102 but fount 101 assertEquals(e.getErrorCode(), 102); } } } 

如果我不使用@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)这些注释,那么我的模拟DAO调用testing工作正常。

我明确需要上面的注释,因为如果我不使用它们,

例如,如果我想执行一个使用多个业务服务的单个任务,那么他们不会发生在一个事务中 。 换句话说,如果一个商业电话使用多个业务服务,说ServiceAServiceB 。 呼叫从serviceAserviceB 。 如果在serviceB发生exception,那么由serviceA完成的数据库更改将不会回滚。

当我使用上面的注释然后上面的例子工作但是模拟DAO调用在junittesting不起作用。

我在pom中有错误的依赖关系吗?

  1. 为什么这不起作用?
  2. 什么解决scheme呢?

Git仓库源代码 ,在这里你会得到示例代码。它在编译时给我一些错误。

我build议你保持简单的testing。 你可以从中获得利益。 有关更多详细信息,请访问Spring文档 :

dependency injection的一个主要优点是它应该使你的代码更容易进行unit testing。 你甚至可以简单地使用new运算符实例化对象,甚至不涉及Spring。 您也可以使用模拟对象而不是真正的依赖关系。

你的testing类应该是这样的。

 public class AddressTest { @Mock private UserDAO userDAO; @Mock private AddressDAO addressDAO; @InjectMocks private AddressService addressServie; @BeforeMethod public void initMock() { addressServie = new AddressServiceImpl(); MockitoAnnotations.initMocks(this); } @Test(dataProvider = "getUser", dataProviderClass = UserDP.class) public void shouldThrowExceptionAddressNotFound(int userId, User user) { when(userDAO.findOne(userId)).thenReturn(user); try { addressServie.findAllAddress(userId); } catch (BadRequestException badRequestException) { assertEquals(badRequestException.getErrorCode(), 102); } } } 

你也应该检查你的实现中的空地址列表。 testing失败,因为提供者类提供了一个没有地址列表初始化的用户实例的testing。

 @Override public List<Address> findAllAddress(int userId) { User user = userDAO.findOne(userId); if (user == null) { throw new BadRequestException("Invalid user id", 101); } List<Address> addresses = user.getAddresses(); if (addresses == null || addresses.isEmpty()) { throw new BadRequestException("Address Not found", 102); } return addresses; } 

你可以尝试使用MockitoJUnitRunner 。

 @RunWith(MockitoJUnitRunner.class) @ContextConfiguration(classes = { TestApp.class }) class MyTestClass{ .. } 

删除所有注释。 你需要一些特殊的东西来使交易成功。

问题:

呼叫从serviceA到serviceB。 如果在serviceB中发生exception,那么由serviceA完成的数据库更改将不会回滚

Spring的事务pipe理器提供了一个独立于技术的API,允许你通过调用getTransaction()方法来启动一个新的事务,并通过

承诺()
回滚()

由于PlatformTransactionManager是事务pipe理的抽象单元,

您为事务pipe理调用的方法保证与技术无关。

  import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; public class TransactionalJdbcBookShop extends JdbcDaoSupport implements BookShop { @Autowired private PlatformTransactionManager transactionManager; 

…..

那么在你的dao方法里面你可以configurationcommit和rollback方法。

  public void purchase(String isbn, String username) { TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { //Your query over here transactionManager.commit(status); } catch (DataAccessException e) { //if the above query fails then transactionManager.rollback(status); throw e; } } 

一个transactionmanager在XMLconfiguration文件中被声明为一个正常的bean。

例如,

以下的beanconfiguration声明了一个DataSourceTransactionManager实例。

它需要设置dataSource属性,以便它可以pipe理由此数据源创build的连接的事务。

 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="bookShop" class="com.apress.springrecipes.bookshop.TransactionalJdbcBookShop"> <property name="dataSource" ref="dataSource" /> <property name="transactionManager" ref="transactionManager" /> </bean> 

我如何在XMLconfiguration文件中使用Spring Boot自动configuration的bean?

你也可以通过github来实现你的应用程序在这里的 bean

一旦你有一个交易定义,

您可以通过调用getTransaction()方法,让事务pipe理器使用该定义启动一个新的事务。

然后它将返回一个TransactionStatus对象来跟踪事务状态。

如果所有的语句都成功执行,你可以让事务pipe理器通过传入事务状态来提交这个事务。

由于Spring JDBC模板抛出的所有exception都是DataAccessException的子类,因此您可以要求事务pipe理器在捕获这种exception时回滚事务。

在这个类中,您已经声明了一般typesPlatformTransactionManager的事务pipe理器属性。

现在你必须注入适当的事务pipe理器实现。

由于您只处理单个数据源并使用JDBC访问它,因此应selectDataSourceTransactionManager

我认为这个问题可能是由注释处理顺序造成的。

您可能可以尝试在方法之前明确地设置您的服务的内部状态,如:

 @Mock UserDAO userDAO; @Autowire AddressService addressServie; @BeforeMethod public void initMock() { MockitoAnnotations.initMocks(this); // using mockito Whitebox org.mockito.internal.util.reflection.Whitebox.setInternalState(addressServie, "userDAO", userDAO); /* or using spring test method org.springframework.test.util.ReflectionTestUtils.setField(addressServie, "userDAO", userDAO);*/ } 

并检查是否仍然出现错误