prosource

서비스에 주석을 달아 @Retention, @Transactional, @Inherited로 테스트한 후 테스트 NG 유닛 테스트가 작동하지 않음

probook 2023. 2. 22. 22:18
반응형

서비스에 주석을 달아 @Retention, @Transactional, @Inherited로 테스트한 후 테스트 NG 유닛 테스트가 작동하지 않음

스프링 부트 어플리케이션에서 TestNG, mockito 유닛 테스트를 통해 비즈니스 서비스를 테스트하고 있습니다.

응용 프로그램은 멀티 모듈 스프링 부트 프로젝트입니다.그리고 비즈니스 모듈의 유닛 테스트를 작성하고 있습니다.

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

내 Test App의 외관상

@SpringBootApplication
public class TestApp{ .... }

My Business Service는 다음과 같습니다.

@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 호출이 제대로 작동합니다.

위의 주석을 명시적으로 사용해야 합니다. 왜냐하면 사용하지 않기 때문입니다.

예를 들어, 여러 비즈니스 서비스를 사용하는 단일 태스크를 수행하려는 경우 단일 트랜잭션으로 수행되지 않습니다.즉, 비즈니스 콜이 복수의 비즈니스 서비스를 사용하는 경우, 다음과 같이 기술합니다.ServiceA ★★★★★★★★★★★★★★★★★」ServiceB은 에서 발신됩니다serviceA로로 합니다.serviceB에서 serviceB은 이 then then then 、 음음 、 음 then 、 then then then then then 。serviceA롤백하지 않습니다.

위의 주석을 사용하면 위의 예제는 작동하지만 junit 테스트에서 모의 DAO 호출은 작동하지 않습니다.

제가 폼에 잘못된 의존관계가 있나요?

  1. 왜 이게 안 되는 거죠?
  2. 이에 대한 해결책은 무엇일까요?

Git Repository Source Code, 여기서 샘플 코드를 얻을 수 있습니다.컴파일 중 오류가 발생합니다.

검사는 단순하게 하는 것이 좋습니다.DI 혜택을 받을 수 있습니다.상세한 것에 대하여는, 다음의 스프링 문서를 참조하십시오.

의존성 주입의 주요 장점 중 하나는 코드 테스트를 쉽게 할 수 있다는 것입니다.스프링을 사용하지 않고도 새 연산자를 사용하여 객체를 간단히 인스턴스화할 수 있습니다.실제 종속성 대신 모의 개체를 사용할 수도 있습니다.

여러분의 시험 수업은 이렇게 되어 있어야 합니다.

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

Mockito를 사용해볼 수 있나요?JUnit Runner 。

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

주석을 모두 제거합니다.거래를 성사시키려면 뭔가 특별한 것이 필요하다.

문제:

콜은 serviceA에서 serviceB로 전송됩니다.serviceB에서 예외가 발생하면 serviceA에 의해 수행된 데이터베이스 변경이 롤백되지 않습니다.

Spring의 트랜잭션 매니저는 getTransaction() 메서드를 호출하여 새로운 트랜잭션을 시작하고 이를 관리할 수 있는 기술에 의존하지 않는 API를 제공합니다.

커밋()
롤백()

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 구성은 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>

XML 컨피규레이션파일에서 Spring Boot 자동 설정 콩을 사용하려면 어떻게 해야 하나요?

또한 github을 통해 앱 에 bean을 구현할 수도 있습니다.

트랜잭션 정의가 확립되면

getTransaction() 메서드를 호출하여 트랜잭션 매니저에게 해당 정의를 사용하여 새로운 트랜잭션을 시작하도록 요청할 수 있습니다.

그런 다음 트랜잭션 상태를 추적하기 위해 TransactionStatus 개체를 반환합니다.

모든 문이 정상적으로 실행되면 트랜잭션 상태를 전달하여 트랜잭션 관리자에게 이 트랜잭션을 커밋하도록 요청합니다.

Spring JDBC 템플릿에 의해 발생하는 예외는 모두 Data Access 서브클래스이기 때문에예외는 트랜잭션 매니저에게 이러한 예외가 발견되었을 때 트랜잭션을 롤백하도록 요청합니다.

이 클래스에서는 일반 유형의 PlatformTransactionManager 트랜잭션 매니저 속성을 선언했습니다.

이제 적절한 트랜잭션 매니저 구현을 삽입해야 합니다.

단일 데이터 소스만 취급하고 JDBC로 액세스하기 때문에 Data Source Transaction Manager를 선택해야 합니다.

저는 그 문제가 주석 처리 순서에 의한 것이라고 생각합니다.

다음과 같은 방법으로 서비스 내부 상태를 명시적으로 설정할 수 있습니다.

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

에러가 아직 발생하는지 확인합니다.

언급URL : https://stackoverflow.com/questions/31315433/testng-unit-test-not-working-after-annotating-service-to-test-with-retention

반응형