在使用@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工作正常。
我明确需要上面的注释,因为如果我不使用它们,
例如,如果我想执行一个使用多个业务服务的单个任务,那么他们不会发生在一个事务中 。 换句话说,如果一个商业电话使用多个业务服务,说ServiceA
和ServiceB
。 呼叫从serviceA
到serviceB
。 如果在serviceB
发生exception,那么由serviceA
完成的数据库更改将不会回滚。
当我使用上面的注释然后上面的例子工作但是模拟DAO调用在junittesting不起作用。
我在pom中有错误的依赖关系吗?
- 为什么这不起作用?
- 什么解决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);*/ }
并检查是否仍然出现错误
- Spring Boot:无法访问本地主机上的REST控制器(404)
- Spring Boot:SpringBootServletInitializer已弃用
- 如何在生产环境中运行一个spring引导可执行文件jar?
- java.lang.NoClassDefFoundError:org / springframework / core / env / ConfigurableEnvironment
- Spring Boot删除白标签错误页面
- 如何在另一个项目中添加依赖项到Spring Boot Jar?
- Spring Boot War部署到Tomcat
- 消息渠道一个或多个?
- 使用YAML的Spring @PropertySource