조금 평범한 개발 이야기

JPA 다중 데이타소스 관리 (3) 본문

개발/JPA 다중 데이타소스 관리

JPA 다중 데이타소스 관리 (3)

jogeum 2018. 9. 9. 19:37

도메인, 저장소 객체

이제 모든 설정이 완료 되었고 정말로 다중 데이타소스 상에서 트랜잭션이 정상적으로 동작이 되는지를 확인해 보겠습니다.

먼저 테스트를 위해 생성해둔 데이타소스에서 사용할 도메인 객체와 저장소 객체를 각각 생성하겠습니다.

데이타소스 1번에서 사용할 도메인과 저장소 객체 입니다.

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity(name = "test_code")
public class TestCode1 {

    @Id
    private String code;
    private String name;

    @Column(name = "create_date")
    private Date createDate;

    public TestCode1(String code, String name) {
        this.code = code;
        this.name = name;
        this.createDate = new Date();
    }
}
@Repository
public interface TestCode1Repository extends JpaRepository<TestCode1, String> {
}

그리고 데이타소스 2번에 사용할 도메인과 저장소 객체도 같이 생성합니다.

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity(name = "test_code")
public class TestCode2 {

    @Id
    private String code;
    private String name;

    @Column(name = "create_date")
    private Date createDate;

    public TestCode2(String code, String name) {
        this.code = code;
        this.name = name;
        this.createDate = new Date();
    }
}
@Repository
public interface TestCode2Repository extends JpaRepository<TestCode2, String> {
}

여기서 주의 깊게 확인할 부분은 TestCode1, TestCode2 도메인 객체에서 사용하는 @Entity 의 이름이 test_code 로 동일하다는 점 입니다. 이것은 데이타소스 1번, 2번에 각각에 test_code 라는 이름의 테이블이 생성이 된다는 의미 입니다.

만약 TestCode1 에 저장을 하면 데이타소스 1번의 test_code 테이블TestCode2 에 저장을 하면 데이타소스 2번의 test_code 테이블에 저장이 됩니다.

 

서비스 객체

그 다음 비지니스 로직을 담당하는 서비스 계층의 객체를 생성해 그곳에서 더미 데이타를 입력한 다음 트랜잭션 테스트가 정상적으로 동작 되는지를 확인해 보겠습니다.

서비스 객체에 각각 TestCode1, TestCode2 저장소 객체를 @Autowired 해 가지고 옵니다.

@Autowired
TestCode1Repository testCode1Repository;

@Autowired
TestCode2Repository testCode2Repository;

 

먼저 성공하는 케이스를 확인하기 위해 임의의 더미 데이타를 생성해 넣는 테스트 코드를 작성합니다.

@Transactional(rollbackFor = Exception.class)
public void saveTestCode() {

    TestCode1 testCode1 = new TestCode1(
            "first",
            "db1 first"
    );

    testCode1Repository.save(testCode1);

    TestCode2 testCode2 = new TestCode2(
            "first",
            "db2 first"
    );

    testCode2Repository.save(testCode2);
}

 

그 다음 강제로 에러를 발생시켜 트랜잭션 처리가 정상적으로 되는지를 확인하는 테스트 코드를 작성합니다.

@Transactional(rollbackFor = Exception.class)
public void rollbackTestCode() {

    TestCode1 testCode1 = new TestCode1(
            "second",
            "db1 second"
    );

    testCode1Repository.save(testCode1);

    TestCode2 testCode2 = new TestCode2(
            "second",
            "db2 second"
    );

    testCode2Repository.save(testCode2);

    throw new RuntimeException("rollback"); // 에러를 강제로 발생 시킵니다.
}

 

 

테스트 환경

마지막으로 테스트를 위해 Junit 을 이용한 환경을 구성합니다.

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MultiTxTestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestCodeServiceTest {

    @Autowired
    TestCodeService testCodeService;

    @Test
    public void saveTestCode() {
        try {
            testCodeService.saveTestCode();
        } catch (Exception e) {
        }

        assertTrue(testCodeService.getTestCode1("first").isPresent());
        assertTrue(testCodeService.getTestCode2("first").isPresent());
    }

    @Test
    public void rollbackTestCode() {
        try {
            testCodeService.rollbackTestCode();
        } catch (Exception e) {
            log.info(e.getMessage());
        }

        assertFalse(testCodeService.getTestCode1("second").isPresent());
        assertFalse(testCodeService.getTestCode2("second").isPresent());
    }
}

먼저 @RunWith 어노테이션으로 Junit 테스트를 진행함을 지정합니다.

그다음 @SpringBootTest 으로 테스트할 클래스를 지정합니다. 여기서는 MultiTxTestApplication.class 즉 웹 애플리케이션 전체를 테스트 합니다.

@SpringBootTest 속성 중 webEnvironmentWebEnvironment.RANDOM_PORT 값을 설정해 테스트가 동작될때 사용할 포트를 기본값으로 지정되어 있는 8080 웹 포트가 아닌 random 하게 생성된 포트로 테스트 되게끔 설정합니다.

추가로 @SpringBootTest 어노테이션은 @RunWith 와 같이 동작되어야 합니다. Junit 테스트를 진행할건데 그게 MultiTxTestApplication.class 기준으로 동작 될 것임을 설명한 거라고 보시면 됩니다.

첫번째 테스트를 보겠습니다. 이 테스트는 정상적으로 데이타가 저장되는 것을 기대하고 작성된 서비스 객체의 saveTestCode 함수를 호출 합니다. 그리고 assertTrue 를 통해 데이타가 정상적으로 저장되어 있는지를 확인합니다.

@Test
public void saveTestCode() {
    try {
        testCodeService.saveTestCode();
    } catch (Exception e) {
    }

    assertTrue(testCodeService.getTestCode1("first").isPresent());
    assertTrue(testCodeService.getTestCode2("first").isPresent());
}

 

두번째 테스트를 보겠습니다. 이 테스트는 강제로 에러를 발생시켜 롤백이 되는 것을 기대하고 작성된, 서비스 객체의 rollbackTestCode 함수를 호출합니다. rollbackTestCode 함수를 호출 했을때 강제로 에러가 발생될것이기 때문에 try catch 구문으로 발생되는 에러를 로깅해 줍니다. 그리고 assertFalse 를 통해 데이타가 롤백되어 저장되지 않았음을 확인합니다.

@Test
public void rollbackTestCode() {
    try {
        testCodeService.rollbackTestCode();
    } catch (Exception e) {
        log.info(e.getMessage());
    }

    assertFalse(testCodeService.getTestCode1("second").isPresent());
    assertFalse(testCodeService.getTestCode2("second").isPresent());
}

이로서 모든 환경 설정과 테스트가 끝났습니다.

 

테스트 코드

예제에서 설명한 테스트 코드는 아래 주소에서 확인하실 수 있습니다.

https://github.com/jogeum/multi-tx-test


Comments