데브코스에서 진행하는 바우처 2주차 미션을 하며
빋은 코드리뷰들을 정리해보려고 합니다.
혹시나 이상한 내용이나 고칠 부분들이 있다면
자유롭게 댓글로 남겨주세요
@SpringJUnitConfig
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
원래 디폴트(LifeCycle.PER_METHOD)는 각 테스트 메소드마다 테스트 클래스의 인스턴스를 새롭게 생성하는데 LifeCycle.PER_CLASS는 새로운 인스턴스를 만들지 않고
하나만 만들어서 각 테스트 메소드마다 같은 인스턴스를 사용해요
위 테스트 코드의 경우
데이터 소스를 읽어서 접근하는데 그 비용이 많이 들기 때문에 한 번만 생성하기 위해서 위 조건을 걸었어요
하지만 뒤태 지존님의 깃허브에 dev-tip에 따라
다시 생각해보니 DB의 읽기만 하는 것이 아니라 데이터를 insert하는 테스트 메서드가 있어서 데이터가 오염될 수 있는 상황을 고려하지 못한 것 같아요
@SpringJUnitConfig
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class JdbcCustomerRepositoryTest {
@Configuration
@ComponentScan(
basePackages = {"com.prgrms.repository.customer"}
)
static class Config {
@Bean
public DataSource dataSource() {
return DataSourceBuilder.create()
.url("jdbc:mysql://localhost/order_byeol")
.username("root")
.password("****")
.type(HikariDataSource.class)
.build();
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(JdbcTemplate jdbcTemplate) {
return new NamedParameterJdbcTemplate(jdbcTemplate);
}
}
기존의 코드입니다.
보시면 제가 직접 데이터 소스 관련 빈들을 만들어서 관계를 만들어서 주입하고 있어요
멘토님께서 이 부분에 대해서
jdbc만을 테스트 하고싶다면
@ JdbcTest 라는 어노테이션에 대해 알아보셔도 좋습니다.
라는 피드백을 주셔서 @JdbcTest에 대해서 찾아보게 되었어요
이 어노테이션은 오직 Jdbc 관련 컴포넌트들에 초점이 맞춰있어요
그래서 전체 auto-configuration 사용이 불가능합니다.
또한 이 어노테이션이 붙은 클래스의 각 테스트마다 transcational과 롤백을 지원된다고 합니다.
그래서 스프링 컨테이너에 제가 등록한 빈을 가져오기 위해서 @Import
을 사용했어요
그리고 마지막으로 임베디드 메모리 데이터 베이스를 사용합니다. 그래서 저의 경우 임베디드 데이터 베이스를 사용하지 않았기 때문에 @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
을 붙여서 외부 데이터 베이스를 연결했어요
그래서 개선된 코드는 아래와 같아요
@JdbcTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import({JdbcCustomerRepository.class, DataRowMapper.class})
class JdbcCustomerRepositoryTest {
private final String TEST_USER_NAME = "test-user";
private final String TEST_USER_EMAIL = "test1-user@gmail.com";
private final int CUSTOMERS_SIZE = 4;
private final int ID = 10;
@Autowired
private JdbcCustomerRepository jdbcCustomerRepository;
*
보다 구체적으로 필드명을 명시해보는 건 어떨까*
을 사용하면 코드를 입력하는 저의 입장에서는 편할지 몰라고 다른 개발자가 저의 코드를 볼 때 어떤 데이터가 들어오는지 다시 한번 데이터 베이스를 살펴봐야 하고
또한 필요없는 속성도 같이 들어오기 때문에 좋지 않은 방법이라고 해요
@Override
public int count() {
return jdbcTemplate.queryForObject("select count(*) from customers", Collections.emptyMap(), Integer.class);
count 쿼리의 경우 where 조건문이 매우 중요하다는 피드백을 받았어요
또한 멘토님께서 항상 테이블에 데이터가 많으면 어떻게 해야할까
라는 고민을 가지고 데이터 베이스를 공부하라고 하셨어요
그래서 count에 대해서 찾아보니 전체 데이터를 읽기 때문에 조건절이 중요하며
데이터의 존재 유무를 판단하기 위해서는 count 대신
조건을 만족하는 데이터가 하나라도 있으면 스캔을 종료하는 exist를 사용하는게 좋다고 해요
그래서 아래와 같이 만들어 보았습니다.
@Override
public boolean existsById(int voucher_id) {
return jdbcTemplate.queryForObject("SELECT 1 EXISTS(SELECT FROM vouchers WHERE voucher_id = :voucher_id)"
, Collections.singletonMap("voucher_id"
, voucher_id), Boolean.class);
}
@DisplayName은 각 단위테스트가 무엇을 반환하는지 어떤 걸 위해서 존재하는지 문서화가 되는 부분이라서
구체적으로 작성해야 한다고 해요
저의 경우
@DisplayName("음수인 경우 예외를 던지는지 테스트한다.")
위와 같이 사용했는데
맨토님께서 아래의 방법으로 예시로 보여주셨어요
바우처의 고정 할인값이 음수가 들어오면 생성할 수 없다
테스트 코드가 필요없는데도 작성한 테스트들이 있어서 이에 대해서 생각해보게 되었습니다.
테스트 코드가 언제 필요한가
제가 생각한 var의 장점은 데이터의 자료형이 바뀌어도 수정할 부분이 없다는 것입니다.
하지만 다른 개발자들과 일하게 된다면 사용하는데 조심해야한다고 생각해요
자료형을 판단할 수 없기 때문에 무엇을 반환하는지 알 수 없고 그래서 코드를 다시 찾아봐야 하는 비용이 발생합니다.
따라서 장점보다 단점이 많기 때문에 사용을 지양해야 한다고 생각해요
위 세 줄이 저의 질문이었는데요
사실 유즈케이스 인터페이스 아키텍처 관련 영상을 시청하고
관련 예시 코드를 보았을 때
어떤 개념인지 정확하게 와닿지 않아서
조금씩 스며들며 학습하기로 했어요
꾸준하게 학습하면 언젠가
할 수 있는 날이 올거라고 생각해요