인프런 워밍업 스터디 클럽 3기 백엔드-code 3주 차 발자국
이 글은 박우빈님의 Practical-Testing 강의 를 참조하여 작성한 글입니다.
어느덧 수료까지 일주일만을 앞두고 있다. 벌써 75%나 진행되었다니,,,
이때까지 강의 안 밀리고 시간 안에 미션 제출한 내 자신 칭찬한다👍🏻 (아직 강의 섹션3개, 미션 2개 남음)
남은 한 주도 열심히 해서 수료해야지!!
이번 주는 테스트 코드가 필요한 이유 및 레이어드 아키텍쳐 내에서 각 레이어드별 테스트 코드 작성하는 법에 대해 배웠다.
강의를 듣고 테스트 코드를 작성해야 하는 이유에 대해서 완전 설득이 되었다.
기존에는 테스트 코드 작성하는 것이 너무 귀찮고 시간이 오래 걸린다는 이유에서 꺼려했지만, 점점 리팩토링 또는 기능을 추가할 때마다 수동으로 테스트 하는게 더욱 귀찮았다. 또한 수동으로 테스트를 했어도 항상 찝찝함이 존재했다.
그래서 우빈님 강의를 들으면서 엄청난 공감을 느꼈고, 강의를 열심히 학습해서,,,많이 배워야겠다는 다짐을 다시 하게된 것 같다.
다음으로 readable-code 강의에서 만든 스터디 카페 이용권 프로그램에 대해 스스로 테스트 코드를 작성해 보는 시간을 가졌다.
이번에는 열심히 작성한 후 리뷰 신청도 하였다.
테스트 코드 관련해서는 리뷰를 한번도 받아본 적이 없기도 하고, 코드 작성 중 궁금한 점이 생겨 리뷰 신청을 하는 용기를 내보았다..ㅋㅋㅋ
다음 주 중간점검 때 리뷰를 해주실 예정인데 기대된다!!!
학습 내용 요약
테스트는 왜 필요할까?
빠른 피드백
리팩토링, 신규 기능 추가 등 변화가 생기는 매순간마다 테스트 코드를 통해, 기존 코드가 정상 동작하는 지 빠르게 피드백을 받을 수 있다.
만약 테스트 코드를 작성하지 않는다면?
변화가 생기는 매순간마다 발생할 수 있는 모든 Case를 고려해야 한다.
변화가 생기는 매순간마다 모든 팀원이 동일한 고민을 해야 한다.
자동화
자동화 테스트로 비교적 빠른 시간 안에 버그를 발견할 수 있고, 수동 테스트에 드는 비용을 크게 절약할 수 있다.(인간이 수동으로 테스트를 하게 된다면 실수할 확률이 매우 높음)
안정성
빠르게 변화하는 소프트웨어에서 테스트 코드를 통해 100%는 아니지만 안정성을 보장할 수 있다.
단위테스트
단위테스트란?
작은 코드 단위를 독립적으로 검증하는 테스트
통합테스트에 비해 준비해야 할 코드가 적으며, 검증 속도가 빠르고 안정적이다.
단위 테스트만으로는 기능 전체의 신뢰성을 보장할 수 없다는 단점이 존재한다.
JUnit5: 단위 테스트를 위한 테스트 프레임 워크
AssertJ: 테스트 코드 작성을 원활하게 돕는 테스트 라이브러리 (풍부한 API, 메서드 체이닝 지원)
TDD: Test Driven Development
선 기능 구현, 후 테스트 작성
테스트 누락 가능성 존재
해피 케이스만 검증할 가능성 존재
잘못된 구현을 늦게 발견할 가능성 존재
선 테스트 작성, 후 기능 구현 (TDD)
복잡도가 낮은, 테스트 가능한 코드로 구현할 수 있게 한다(유연하며 유지보수 쉬운 코드)
쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해줌
구현에 대한 빠른 피드백을 받을 수 있음 -> 과감한 리팩토링 가능해짐
Spring / JPA 훑어보기 & 기본 엔티티 설계
라이브러리
내 코드가 주체가 돼서 필요한 기능이 있다면 외부에서 끌어와서 사용하게 되는데, 이 때 외부에 있는 것들을 라이브러리라 한다.
따라서 라이브러리는 내 코드가 주체가 되는 환경이고 능동적인 특징을 지닌다.
프레임워크
이미 갖춰진 동작할 수 있는 그런 환경들이 구성이 돼 있고, 내 코드가 이 프레임 안에 들어가는 수동적인 역할을 한다.
따라서 프레임워크에서는 내 코드가 수동적인 존재가 된다.
ORM
객체 지향 프로그래밍(OOP)과 관계형 데이터베이스(RDB) 간의 구조적 차이를 해소하기 위해 사용되는 기술이다.
데이터베이스에 CRUD하는 작업을 객체 기반으로 처리함으로써 개발자들이 기본적인 쿼리 작성하는 단순 작업을 줄이고 비지니스 로직에 집중할 수 있게 해준다.
JPA
Java진영의 ORM 기술 표준
인터페이스이며, 여러 구현체가 있지만 주로 Hibernate를 많이 사용한다.
Spring 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA제공한다.
Persistence Layer 테스트
데이터에 접근하는 역할로 데이터 CRUD와 연관된 메서드들이 위치해 있다.
비지니스 로직이 포함돼서는 안됨!
@DataJpaTest
JPA와 관련된 의존성들만 주입해준다 -> @SpringBootTest보다 가볍다.
어노테이션 내부에 @Transactional이 포함되어 있어 테스트 후 데이터가 롤백된다.
Business Layer 테스트
비지니스 로직에 관련된 메서드들이 위치해 있다.
@Transactional
vssql
을 이용한 데이터 삭제실제 코드에서는 @Transactional을 사용하지 않는데, 단순히 롤백만을 위해 테스트코드에서 @Transactional을 사용하면, 실제 작동 방식과 다르게 작동할 수 있다.
따라서 테스트 코드 작성시 Transactional의 부작용에 대해 인지하고 사용할 것!!
리포지토리 테스트와 로직이 많이 없는 얇은 서비스 테스트는 결이 비슷하다
왜냐하면 리포지토리에 대한 결이 그대로 오기 때문이다.
하지만
서비스가 더 기능이 추가될수록 발전을 하기 때문에, 동일한 테스트라고 생각이 되더라도 작성을 하는 게 좋다! (검증이 추가될 수도 있기 때문)
클래스 상단에
@Transactional(readOnly = true)
로 표현하고 실제 트랜잭션이 사용되는 메서드에@Transactional
로 표현해주기!
미션
Day 11 - 스터디 카페 이용권 선택 시스템 테스트 코드 작성하기
나의 코드: https://github.com/Jiihyun/readable-code/pull/3
🤔 고민사항
1. StudyCafeSeatPassTest 클래스
테스트 케이스 반복을 줄이기 위해 @CsvSource
를 사용해 보았다.
이로 인해 데이터만 추가하면되니 테스트 케이스를 쉽게 확장할 수 있다는 장점을 느꼈다.
하지만 StudyCafePassType을 직접 문자열로 작성했다보니, 후에 passType이 수정될 경우 테스트 코드 내 passType을 직접 수정해야 하기 때문에 유지보수 비용이 증가할 것 같다는 생각이 들기도 했다..
그래서 현업에서는 어떤 방식으로 테스트를 하지? 하는 궁금증이 생겼던 것 같다.
두 방법에 대해 트레이드오프가 존재하는데 어떤 걸 더 중요시하게 여겨 테스트하는지 궁금하다!
2. FileReaderTest 클래스
파일을 잘 읽어와 데이터를 의도한 대로 파싱하였는지 테스트하고 싶었다.
하지만 반환 타입이 일급컬렉션으로 되어 있고,컬렉션 내의 데이터를 확인하는 메서드는 프로그램에서 사용되지 않기 때문에 존재하지 않았다.
테스트를 공부하면서 배운 것 중 하나는 테스트만을 위한 메서드는 최대한 자제해야 한다고 했다.
그래서 오로지 테스트를 위해 컬렉션의 크기 등을 확인할 수 있는 메서드를 추가하고 싶지 않았고, 그래서 일급 컬렉션의 메서드를 직접 호출하여 테스트를 진행했다.
그치만 이렇게 짜여진 테스트도 본 적이 없는데....이런 방식으로 테스트를 해도 괜찮은지 궁금하다 ㅋㅎㅎㅋ
마무리
앞으로 이제 일주일 남았다..!! 벌써...?
다음 주 학습 내용에는 평소에 궁금했던 내용들에 대해 다루기 때문에 매우 기대가 된다.
배워본 적 없는 내용이라 시간을 많이 투자해야 할 것 하지만,,,
열심히 학습해서 내 것으로 만들어야지!
댓글을 작성해보세요.