본문 바로가기
📝 회고/✅ 22년 회고

[일일 회고] 22.01.04.화 - 테스트 코드 리팩토링과 개발 생각

by kukim 2022. 1. 4.

👍 Keep

  • 어제 구현한 테스트 코드 리팩토링!
  • 중간에 스트레칭하며 공부
  • 공부한 내용 문서화 & 블로깅 완료
  • 팀원들과의 프로젝트 코드 리뷰 & 토크( +a 간단한 단위 테스트 소개와 사용방법 발표(?))

🔥Problem

  • 아직도 1~2시간 공부하고 10분 쉰다.
  • 운동 Pass
  • 문서 작성에 시간 투자가 많음

🚒 Try

  • 한 시간 휴식 알람이 울리면 하던 거 멈추고 당장 쉬기
  • 운동을 쉬는 시간 중간에라도 하자! (푸시업)
  • 사소한 내용까지 다 문서화하지 말자

📚TIL

개발 생각

- 프로젝트에서 요구사항이 주어졌을 때 보통 비기능적 요구사항은 주어지지 않는다. 예를 들어 가독성이 좋고 유지보수성이 뛰어나며 효율적이고 간결한 코드에 대한 요구사항은 없다고 봐도 무방하다. 요구사항으로 주어진 기능들을 코딩하며 비기능적 요구사항까지 생각하며 코딩하자. 

- 무엇보다 코딩할 때 왜 이 선택을 했는지 그 이유와 기록(commit)을 남기자

- 가독성 높은 코드 작성을 위해 앞으로의 마음가짐은 지금 작성하는 이 코드는 내일 내가 보는 것이 아닌 마동석님 께서 보신다 생각하고 작성하자(제대로 안 쓰면 맞는 거야)

- 보통 학습 단계에서의 프로젝트들은 바텀-업 형태로 문제가 주어지기 때문에 모듈화(재사용성) 하기 편하다. 하지만 실무의 요구사항은 탑-다운 형태로 주어지고 그 문제를 잘게 쪼개서 모듈화해야 한다. 무엇이든 주어지면 처음부터 완벽한 설계는 할 수 없다고 한다. 작은 문제로 쪼개고 하나씩 풀어간다면 해결할 수 있지 않을까? (TDD의 장점이 바로 작은 것부터 하나씩 구현해 가며 설계를 빠르게 피드백받을 수 있다는 것도 여기에 있지 않을까?)

테스트 코드 리팩토링

책 '단위 테스트' 3장까지 읽고 어제 구현한 프로젝트에 적용시켜보았다.

기존 테스트 코드

public class mission1Tests {
    Gate gate;

    @BeforeEach()
    void init() {
        gate = new Gate();
    }

    @Test
    @DisplayName("AND 게이트 테스트")
    void AndGateTest() {
        assertFalse(gate.and(false, false));
        assertFalse(gate.and(false, true));
        assertFalse(gate.and(true, false));
        assertTrue(gate.and(true, true));
    }

// 중략

    @Test
    @DisplayName("XOR 게이트 테스트")
    void XorGateTest() {
        assertFalse(gate.xor(false, false));
        assertTrue(gate.xor(false, true));
        assertTrue(gate.xor(true, false));
        assertFalse(gate.xor(true, true));
    }

}

✅ 테스트 픽스처 초기화(BeforeEach -> 팩토리 메서드로 변경)와 sut 변수명 사용

어제는 나름대로 생성자를 통해 인스턴스를 만들고 테스트하면 의존성이 높아지기 때문에 @BeforeEach()를 사용해서 매 테스트마다 인스턴스를 생성해주었다. 단위 테스트 3장의 내용과, 향로님의 테스트 픽스처 올바르게 사용하기 글을 보고 팩토리 메서드로 변경하는 것이 좋다고 생각하여 수정했다. 변경은 좋았으나 사실 위 테스트 같은 경우 너무나 간단한 경우이기 때문에 @BeforeEach()로 써도 문제없어 보이지만... 그래도 핵심은 테스트 간 결합도를 낮추고, 가독성을 높이고, 전체 테스트 코드 양을 줄이고, 재사용성을 높이는 것은 중요하다.

 

테스트 코드에서 어디가 SUT(System Under Test, 테스트 대상 시스템)인지 가독성을 높이기 위해 해당 변수를 'sut'로 정했다.

public class mission1Tests {
// 팩토리 메서드
    private Gate createGate() {
        return new Gate();
    }
    
    // ... 중략
    
    @Test
    void andGateTest(boolean paramA, boolean paramB, boolean expected) {
        Gate sut = createGate(); // 팩토리 메서드 사용과 sut 변수명 지정

		// ... 중략 ...
    }

 

✅  AAA 패턴 적용

기존 테스트 코드에 구조가 없었다면 AAA 패턴을 적용하여 테스트 코드의 구조와 일관성을 주었다.

public class mission1Tests {

    private Gate createGate() {
        return new Gate();
    }
    
    void andGateTest(boolean paramA, boolean paramB, boolean expected) {
        Gate sut = createGate();

        boolean result = sut.and(paramA, paramB);

        assertThat(result).isEqualTo(expected);
    }
    // 생략
}

 

✅  간단한 파라미터 반복 테스트를 Parameterized Test로 대체, AssertJ 사용

Parameterized Test

메서드의 모든 예를 테스트하기 위해 하나의 테스트에 여러 개의 assert를 넣어줬지만 가독성이 떨어졌다. → 매개변수화된 테스트(parameterized test)를 사용하여 유사한 테스트에 대한 가독성, 재사용성과 테스트 분리를 해주었다.

자바에서 @ParameterizedTest와 @MethodSource를 통해 매개변수 테스트와 그 인자 값을 쉽게 관리, 넣어줄 수 있다.

 

AssertJ의 assertThat() 사용

자바 JUnit5 경우 자체 assert 메서드가 있는데 가독성이 떨어지거나 사용법이 복잡하기 때문에 AssertJ를 사용하여 가독성과 편의성을 높였다. 앞으로 AssertJ로 테스트를 작성하려 한다.  (참고 : 백기선님 : AssertJ가 JUnit의 assertThat 보다 편리한 이유)

 

public class mission1Tests {
    private Gate createGate() {
        return new Gate();
    }

// 기존 테스트
    @Test
    @DisplayName("AND 게이트 테스트")
    void AndGateTest() {
        assertFalse(gate.and(false, false));
        assertFalse(gate.and(false, true));
        assertFalse(gate.and(true, false));
        assertTrue(gate.and(true, true));
    }


// Parameterized 테스트
    @DisplayName("AND 게이트 테스트")
    @ParameterizedTest(name = "{index} : {0} AND {1} = {2}")
    @MethodSource("paramAndGate")
    void and_gate(boolean paramA, boolean paramB, boolean expected) {
        Gate sut = createGate();

        boolean result = sut.and(paramA, paramB);

        assertThat(result).isEqualTo(expected); // AssertJ
    }

    private static Stream<Arguments> paramAndGate() {
        return Stream.of(
                Arguments.of(false, false, false),
                Arguments.of(false, true, false),
                Arguments.of(true, false, false),
                Arguments.of(true, true, true)
        );
    }
}

블로깅

책 단위 테스트 ch3까지 요약정리 완료. 책만 보는 것보다 그 내용을 활용하는 것이 더 중요하다.

📗 책 '단위 테스트(Unit Test)'를 공부하며

📗 단위 테스트 1장 : 단위 테스트의 목표

📗 단위 테스트 3장 : 단위 테스트 구조

댓글