1. 경계란
- 오픈소스, 라이브러리를 안쓰는 프로젝트는 없습니다.
- 외부 코드를 내 코드에서 호출하는 부분을 경계라고 합니다. 단순하게 외부 코드를 사용하려는 곳에서 직접 호출할 수 있지만 보통 외부 코드를 호출하는 부분을 따로 만드는게 보편적인 방법 입니다.
2. 캡슐화(Encapsulation)
- 객체의 실체 구현을 외부로부터 감추는 방식
1) 예제 1. 나쁜 예
Map<String, Sensor> sensors = SensorFactory().get();
Sensor s = sensor.get(sensorId);
- 외부 라이브러리 SensorFactory는 Map<String, Sensor> 형태로 값을 리턴해준다고 가정해봅시다.
- Map에서 기본으로 제공해주는 메서드(clear, remove)로 인해 값이 조작되거나 추가될 수 있는 문제가 발생합니다.
2) 예제 2. 좋은 예
public class Sensors {
private Map<String, Sensor> sensors = SensorsFactory().get();
public Sensor getById(String id) {
return (Sensor) sensors.get(id);
}
// ..
}
- 캡슐화를 통해서 SensorsFactory()를 감춘다.
- Sensors 경계 클래스를 만들어 외부 코드 SensorsFactory().get()에서 리턴값을 조작할 수 있도록 합니다.
- 외부 코드를 경계 클래스로 랩핑해서 불필요한 기능을 제한하고 내부 로직에 맞게 사용합니다.
3. 경계 짓기
1) 외부 코드와 호환하기 : Adapter 패턴
- 외부 코드를 호출할 때, 우리가 정의한 인터페이스대로 호출하기 위해 사용하는 패턴
2) 외부 라이브러리 테스트하기 Learning Test
- 외부 코드를 배우고, 안정성도 미리 검증할 수 있습니다.
- Learning Test를 작성해 라이브러리를 테스트하면 좋습니다.
4. 단위 테스트
- 유닛 테스트(unit test)는 컴퓨터 프로그래밍에서 소스 코드의 특정 모듈이 의도된 대로 정확히 작동하는지 검증하는 절차 입니다. 모든 함수와 메소드에 대한 테스트 케이스를 작성하는 절차를 의미합니다.
- 개별 컴포넌트가 정상적으로 작동하는지 테스트 케이스를 만들어 테스트 하는 것을 의미합니다.
- 주로 JUnit, Mockito를 사용해 테스트를 합니다.
5. 깨끗한 테스트 코드를 유지해야 하는 이유
TDD 법칙
첫째 법칙 : 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
둘째 법칙 : 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
셋째 법칙 : 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성합니다.
- 위 세가지 규칙을 따르면 개발과 테스트가 대략 30초 주기로 묶인다. 이렇게 일하면 실제 코드를 사실상 전부 테스트하는 테스트 케이스가 나온다. 하지만 실제 코드와 맞먹을 정도로 방대한 테스트 코드는 심각한 관리 문제를 유발하기도 합니다.
- 지저분한 테스트 코드는 내놓는 것은 테스트를 안 하는 것보다 못합니다. 만약 실제 코드가 변경되었을 때, 테스트 코드도 바뀌어야 한다면 테스트 코드는 계속해서 늘어나는 부담이 됩니다.
- 테스트 코드는 실제 코드 못지 않게 중요합니다.
6. 깨끗한 테스트 코드
- 깨끗한 테스트 코드를 만드려면, 3가지가 필요합니다. 가독성, 가독성, 가독성~! 입니다.
- 기본적인 F.I.R.S.T. 원칙이 있는데, 잘 따르면 좋습니다.
- Fast : 테스트는 빨라야 합니다. 테스트는 자주 돌려야 하기 때문에 느리면 안됩니다.
- Independent : 각 테스트는 서로 의존하면 안 됩니다. 의존되어 있으면 테스트가 연달아 실패하기 때문에 원인을 진단하기 어렵습니다.
- Repeatable : 어떤 환경에서든 반복가능해야 합니다. local, QA, Staging, 운영환경 어디서든 테스트가 돌아가야 합니다.
- Self-validating : 테스트는 bool값으로 결과를 내야 합니다.
- Timely : 테스트는 적시에 작성해야 합니다. 단위 테스트는 테스트 하려는 실제 코드를 구현하기 직전에 구현합니다.
1) 테스트 코드 나쁜 예시
@Test
public void testTeacher() {
Student student1 = studentRepository.save(new Student("김노력", 1));
assertEquals("김선생", student1.teacher.name);
Student student2 = studentRepository.save(new Student("정노력", 2));
assertEquals("정선생", student2.teacher.name);
Student student3 = studentRepository.save(new Student("강노력", 3));
assertEquals("강선생", student3.teacher.name);
}
- 학생의 이름과 반을 저장한 후 그 학생의 선생님 이름을 비교하는 테스트 입니다.
- 만약 위 테스트에서 2반 테스트가 실패했다면 어떻게 알 수 있을까요? 하나씩 찍어봐야...ㅎ
2) 테스트 코드 좋은 예시
@Test
public void test1Ban() {
Student student1 = studentRepository.save(new Student("김노력", 1));
assertEquals("김선생", student1.teacher.name);
}
@Test
public void test2Ban() {
Student student2 = studentRepository.save(new Student("정노력", 2));
assertEquals("정선생", student2.teacher.name);
}
@Test
public void test3Ban() {
Student student3 = studentRepository.save(new Student("강노력", 3));
assertEquals("강선생", student3.teacher.name);
}
- 개념 당 하나의 테스트를 작성하니, 어떤 테스트가 실패했는지 알 수 있습니다.
7. 테스트 코드 작성 할 때 순서
- Build, Given : 첫 부분은 테스트 자료를 만듭니다.
- Operate, When : 두 번째 부분은 테스트 자료를 조작합니다.
- Check, Then : 세 번째 부분은 테스트를 검증합니다.
@Test
public void testAddStudent() {
// build, given
Student student = new Student("effortGuy", 1);
// operate, when
studentRepository.save(student);
// check, then
assertThat(studentRepository.findAll().size(), 1);
}
결론
- 사실상 깨끗한 테스트 코드라는 주제는 책 한 권을 할애해도 모자랍니다.
- 테스트 코드는 실제 코드만큼이나 프로젝트 건강에 중요합니다. 어쩌면 실제 코드보다 더 중요할지도 모릅니다. 그러므로 테스트 코드는 지속적으로 깨끗하게 관리하려는 노력이 필요합니다.
728x90
'Software Tech > Spring (feat.JAVA)' 카테고리의 다른 글
[클린코드] 6. 시스템 (1) | 2025.05.20 |
---|---|
[클린코드] 5. 클래스 (1) | 2025.05.20 |
[클린코드] 3. 예외처리 (0) | 2025.05.19 |
[클린코드] 2. 주석 & 형식 맞추기 & 객체와 자료구조 (1) | 2025.05.17 |
[클린코드] 1. 깨끗한 코드와 함수 (1) | 2025.05.15 |