Подтвердить что ты не робот

TestNG unit test не работает после аннотирования службы для тестирования с помощью @Retention, @Transactional, @Inherited

Я тестирую бизнес-сервис с TestNG, модульные тесты mockito в приложении загрузки spring.

Приложение представляет собой многомодульный проект загрузки spring. И я пишу модульные тесты для бизнес-модуля.

Я добавил следующие зависимости, связанные с тестированием в 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>

Моя аннотация оболочки выглядит как

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

My 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), @Inherited эти аннотации, то мои макетные вызовы DAO в тесте работают нормально.

Мне нужно больше аннотаций, потому что, если я их не использую,

Например, если я хочу выполнить одну задачу, которая использует несколько бизнес-сервисов, то они не будут выполняться в транзакции ONE. Другими словами, если бизнес-вызов использует несколько бизнес-сервисов, скажем ServiceA и ServiceB. Звонок идет от ServiceA до ServiceB. Если в ServiceB возникает исключение, то изменения базы данных, сделанные ServiceA, не будут отменены.

Когда я использую выше аннотации, то выше пример работает, но макетирование вызовов DAO в тестах junit не работает.

Есть ли у меня неправильные зависимости в pom?

  • Почему это не работает?
  • Каким будет решение?

Git Исходный код репозитория, здесь вы получите образец кода. Это дает мне некоторую ошибку, пока компилирование.

4b9b3361

Ответ 1

Я рекомендую вам проводить тесты просто. Вы можете воспользоваться преимуществами преимуществ DI. Для получения дополнительной информации посетите Spring документацию:

Одним из основных преимуществ инъекции зависимостей является то, что он должен сделать ваш код проще unit test. Вы можете просто создавать объекты с помощью нового оператора, даже не используя Spring. Вы также можете использовать макетные объекты вместо реальных зависимостей.

Ваш тестовый класс должен выглядеть так.

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);
        }
    }
}

Вы также должны проверить список нулевых адресов в своей реализации. Тест не выполняется, потому что класс поставщика предоставляет тест с экземпляром пользователя, который не имеет инициализированного списка адресов.

@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;
}

Ответ 2

Удалите все свои аннотации. Для совершения транзакций вам нужно что-то особенное.

Проблема:

Звонок переходит из службы A в службу Б. Если исключение происходит в serviceB, то изменения базы данных, сделанные службой, не будут откатываться

Spring менеджер транзакций предоставляет независимый от технологии API, который позволяет вам запускать новую транзакцию, вызвав метод getTransaction() и управляя им с помощью

фиксации()
откат()

Поскольку PlatformTransactionManager является абстрактным модулем для управление транзакциями,

гарантированы методы, которые вы указали для управления транзакциями быть независимой от технологии.

    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 вы можете настроить метод фиксации и отката.

    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;
    }
    }

В файле конфигурации XML объявляется транзакционный менеджер как обычный bean.

Для Например,

следующая конфигурация bean объявляет экземпляр DataSourceTransactionManager.

Для этого требуется свойство dataSource, чтобы оно могло управлять транзакциями для соединений по этому источнику данных.

<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>

Как я могу использовать Spring загрузочную автоматическую конфигурацию beans в файлах конфигурации XML?

Вы также можете пройти через github для реализации bean внутри вашего приложения через здесь

После определения транзакции

Вы можете попросить менеджера транзакций начать новую транзакцию с этим определением, вызвав метод getTransaction().

Тогда это будет вернуть объект TransactionStatus для отслеживания состояния транзакции.

Если все утверждения выполнить успешно, вы попросите менеджера транзакций совершить эту транзакцию путем передачи в состоянии транзакции.

Поскольку все исключения, создаваемые шаблоном JDBC Spring, являются подклассами DataAccessException, вы просите транзакционного менеджера отменить транзакцию, когда этот вид исключения пойман.

В этом классе вы объявили свойство менеджера транзакций общего типа PlatformTransactionManager.

Теперь вам нужно ввести соответствующий менеджер транзакций реализация.

Поскольку вы имеете дело только с одним источником данных и получаете доступ к нему с помощью JDBC, вам следует выбрать DataSourceTransactionManager.

Ответ 3

Можете ли вы попробовать использовать MockitoJUnitRunner.

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

Ответ 4

Я думаю, что проблема может быть вызвана порядком обработки аннотаций.

Вероятно, вы можете попытаться установить внутреннее состояние вашей службы явно перед таким способом:

@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);*/
}

и проверьте, не происходит ли ошибка