java - starter - La prueba de la unidad TestNG no funciona después de anotar el servicio para probar con @Retention, @Transactional, @ Heredado
spring-boot-starter-test example (4)
¿Puedes intentar usar MockitoJUnitRunner ?
@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(classes = { TestApp.class })
class MyTestClass{
..
}
Estoy probando un servicio comercial con TestNG, pruebas de unidad de mockito en la aplicación de arranque de primavera.
La aplicación es un proyecto de arranque de primavera de varios módulos. Y estoy escribiendo pruebas de unidades para el módulo de negocios.
He agregado las siguientes pruebas relacionadas con dependencias en pom,
<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>
Mi anotación de envoltura se ve como
@Service
@Transactional
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyServiceAnnotation{}
Mi TestApp se parece a
@SpringBootApplication
public class TestApp{ .... }
Mi servicio comercial parece
@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 se parece a
@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);
}
}
}
Si no utilizo @Target(ElementType.TYPE)
, @Retention(RetentionPolicy.RUNTIME)
, @Inherited
estas anotaciones, entonces mis llamadas simuladas de DAO en la prueba funcionan bien.
Necesito las anotaciones anteriores explícitamente porque si no las uso, entonces
Por ejemplo, si quiero realizar una única tarea que utiliza múltiples servicios comerciales, entonces no ocurrirá en UNA transacción . En otras palabras, si una llamada comercial usa múltiples servicios comerciales, diga ServiceA
y ServiceB
. La llamada va de serviceA
a serviceB
. Si se produce una excepción en serviceB
, los cambios en la base de datos realizados por serviceA
no se revertirán.
Cuando uso las anotaciones anteriores, el ejemplo anterior funciona PERO las llamadas simuladas a DAO en las pruebas junit no funcionan.
¿Tengo dependencias incorrectas en pom?
- ¿Por qué esto no está funcionando?
- ¿Cuál sería la solución sobre esto?
Código fuente del repositorio de Git , aquí obtendrá un código de muestra. Me está dando un error al compilar.
Creo que ese problema puede deberse al orden de procesamiento de la anotación.
Probablemente pueda intentar establecer el estado interno de su servicio explícitamente en el método before before like like:
@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);*/
}
y verificar si aún se produce un error
Elimina todas tus anotaciones. Necesita algo especial para que las transacciones funcionen.
Problema:
La llamada va de serviceA a serviceB. Si se produce una excepción en serviceB, los cambios en la base de datos realizados por serviceA no se revertirán
El administrador de transacciones de Spring proporciona una API independiente de la tecnología que le permite iniciar una nueva transacción llamando al método getTransaction () y administrándolo por
cometer()
Retroceder()
Como PlatformTransactionManager es una unidad abstracta para la gestión de transacciones,
los métodos que solicitó para la administración de transacciones están garantizados por ser independientes de la tecnología.
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;
.....
luego dentro de su método dao puede configurar el método de confirmación y restitución.
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;
}
}
Un transactionmanager se declara en el archivo de configuración XML como un bean normal.
Por ejemplo,
la siguiente configuración de beans declara una instancia de DataSourceTransactionManager.
Requiere que se establezca la propiedad dataSource para que pueda gestionar transacciones para las conexiones realizadas por esta fuente de datos.
<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>
¿Cómo puedo usar los beans autoconfigurados de Spring Boot en archivos de configuración XML?
También puede ir a través de github para implementar bean dentro de su aplicación here
Una vez que tenga una definición de transacción,
Puede solicitar al gerente de transacciones que inicie una nueva transacción con esa definición llamando al método getTransaction ().
A continuación, devolverá un objeto TransactionStatus para realizar un seguimiento del estado de la transacción.
Si todas las declaraciones se ejecutan correctamente, solicite al gerente de transacciones que confirme esta transacción al pasar el estado de la transacción.
Como todas las excepciones lanzadas por la plantilla Spring JDBC son subclases de DataAccessException, le pide al administrador de la transacción que retrotraiga la transacción cuando se detecta este tipo de excepción.
En esta clase, ha declarado la propiedad del administrador de transacciones del tipo general PlatformTransactionManager .
Ahora debe inyectar una implementación apropiada del administrador de transacciones.
Como se trata solo de una única fuente de datos y de acceder a ella con JDBC, debe elegir DataSourceTransactionManager .
Te recomiendo que mantengas las pruebas simples. Puede aprovechar los beneficios DI. Para más detalles, visite la documentación de Spring :
Una de las principales ventajas de la inyección de dependencia es que debe hacer que su código sea más fácil para la prueba unitaria. Simplemente puede instanciar objetos usando el nuevo operador sin siquiera involucrar a Spring. También puede usar objetos simulados en lugar de dependencias reales.
Tu clase de prueba debería verse así.
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);
}
}
}
También debe verificar la lista de direcciones nulas en su implementación. La prueba falla porque la clase de proveedor proporciona la prueba con una instancia de usuario que no tiene la lista de direcciones inicializada.
@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;
}