티스토리 뷰

 

왜 테스트 코드를 작성해야할까? 

그리고 어떻게 작성하면 좋을까?

한 번 생각하고 알아보자.  

 

 

why 한 방울,, 열정 한 방울,,

 

 

Test code란?

소프트웨어의 기능과 동작이 개발자가 본래 예상하고 원하는대로 수행하는가 테스트하는 코드다.

 

 

왜 Test code를 만들어야 할까? 

만약 1000줄의 코드를 작성했다고 치자.

입력은 20줄을 받고 출력이 이제 한 줄 나올 것이다.(너무 극단적인가ㅎ)

그럼 이제 해당 프로그램이 정상 동작하는지 확인 할 때마다 20줄을 입력해야할까?

그리고 중간중간에 생기는 에러에 대해서 코드 사이사이에 동작 확인 용 출력 메시지를 만들어야할까? 

 

물론 작은 기능에서는 가능할 수도 있다고 본다. 

그러나 해당 행위가 효율적인가에 대해서 문제를 놓고 봤을 때,

비효율적이다 생각한다. 

 

다시금 왜 테스트 코드를 작성해야하는가에 대해 생각해보면 

Test code는 프로그래밍 중에 생길 수 있는 버그나, 리펙토링에 있어서 

개발자가 본래 예상하고 원하는대로 수행하는가에 대해 효율적으로 확인할 수 있는 큰 이득을 주게된다. 

그리고 더 나아가 협업을 함에 있어서 본인이 작성한 코드가 올바르게 동작한다는 것을 팀원에게 증명할 수 있는 자료가 되기도 한다. 

 

 

Test code의 장점을 살려서 작성하는 방법은 무엇일까? 

작은 모오오든 기능들에 대해 테스트 코드를 작성해야한다고는 생각하지 않는다.

(물론 test coverage가 높으면 높을 수록 안정성이 보장된 코드라 생각한다.) 

 

그러나 프로그래밍은 언제나 시간의 제약을 받기에, 

가치가 높은 테스트 코드를 우선시 하여 작성하는게 좋다고 생각한다. 

 

그럼 가치 높은 테스트 코드를 작성하는 방법이 무엇이며

어떻게 최대한 이득을 취할 수 있을까?

 

 

테스트 주도 개발 TDD(Test Driven Development)을 하면 된다. 

TDD는 테스트 코드를 먼저 작성하고 이후에 해당 메서드를 하나씩 구현해 나가는 방식이다. 

그럼 어떻게 이득을 얻을까? 

 

 

이전 포스트에서 만들기로 했던 랜덤 숫자 생성 기능을

직접 TDD로 구현해보면서 이득을 찾아보자. 

 

 

 Computer객체를 만든 뒤, 

먼저 랜덤 숫자 기능에 대한 테스트 코드를 만든다.  

 

 

빨간 색이 참 많다.

 

 

아직 Computer 클레스도 생성되지 않았다. 

그리고 Computer 클레스 인스턴스 변수로 존재할 randomNumbers 도 없다. 

 

해당 테스트를 일단 통과하기 위해 동작없이 결과만을 반환하도록 만든다. 

 

 

Computer 작성

 

 

해당 테스트가 원하는 값을 반환할 때 통과하는지 돌려본다! 

 

성공!

 

이전에 빨간 코드들이 흰색으로 돌아왔으며 
테스트는 개발자(김남김)이 원하는값을 반환할 경우 정상 동작하는 테스트를 확인했다. 

 

그럼 이제 해당 테스트가 정상 동작함을 알았으니, 클레스 내부를 진짜로 구현해보도록 하자. 

 

 

 

이전에 List.of(1, 2, 3)를 반환하던 getRandomNumbers를 구현해봤고

테스트가 성공하였으므로 해당 메서드는 개발자의 요구대로 동작함을 확인했다. 

 

그런데, 잠깐 요구 사항로  indent depth를 2이하로 제한해보자.

 

 

현재 위에 getRandomNumbers는 딱 2인데도 읽는데 조금 불편하다.
그럼 public으로 쓰이는 해당 메서드를 private한 메서드로 나눠 추상화 시키도록 하자. 

 

먼저 getRandomNumbers  안에서 가장 작은 행위인

인스턴스 변수 randomNumbers 에 랜덤한 값을 넣는 행위를 빼낸다. 

 

 

 

 

해당 행위를 빼니, 다음과 같이 ident depth가 1로 줄었고 while문안에 코드가 추상화되어 메서드 네임을 읽는 것 만으로 코드 흐름을 읽을 수 있게되었다!

 


그런데 다시 생각해보자. 

 

 

 

 

위에 테스트 코드를 보면

비즈니스스 로직에서도 해당 클레스를 생성하고 인스턴수 변수를 불러오는 방식으로 사용할 것이다. 

 

그럼 Computer 생성시에 randomNumbers를 초기화 하도록 해야되는 것 아닐까?

getRandomNumbers는 네이밍 그 자체로 해당 클레스의 인스턴스 변수를 주는 것인데,

초기화 작업과 랜덤 숫자를 넣는 행위마저 갖고 있다. (두개를 하고 있었다. ) 

 

 

그럼 이걸 어떻게 더 나눌 수 있을까? 

먼저, 네이밍 대로 동작하기 위해 getRandomNumbers 메서드는 randomNumbers 만을 가져오도록 수정한다. 

 

 

 

 

인스턴스 변수를 초기화 하는 작업을 분리하고자 initializeRandomNumbers 메서드를 만들고,  

이전에 만든  addRandomNumberToRandomNumbers를 부르도록 한다. 

 

 

 

 

그리고 생성시에 초기화 작업을 수행할 수 있도록 하며 

인스턴스 변수에 대해서도 제어자를 달아주고 이전의 3에 대해 무슨 뜻인지 알 수있도록 상수로 선언 한다. 

 

 

 

 

 

그렇게 Computer 클레스의 전체 모습을 보면 아래와 같다. 

package baseball.domain;

import camp.nextstep.edu.missionutils.Randoms;

import java.util.ArrayList;
import java.util.List;

public class Computer {
    private static final Integer LENGTH_OF_NUMBER_SIZE = 3;
    private final List<Integer> randomNumbers;

    public Computer(){
        randomNumbers = new ArrayList<>();

        initializeRandomNumbers();
    }

    public List<Integer> getRandomNumbers() {
        return randomNumbers;
    }

    private void initializeRandomNumbers() {
        while (randomNumbers.size() < LENGTH_OF_NUMBER_SIZE ) {
            addRandomNumberToRandomNumbers();
        }
    }

    private void addRandomNumberToRandomNumbers() {
        int randomNumber = Randoms.pickNumberInRange(1, 9);

        if (!randomNumbers.contains(randomNumber)) {
            randomNumbers.add(randomNumber);
        }
    }
}

 

 

그럼 리펙토링을 거친 이 코드는 정상 동작을 할까? 

테스트 코드가 이미 구현되어 있기 때문에, 테스트 코드를 실행시켜 통과되는 것만으로 

개발자(나)가 바라는대로 기능이 동작하는 것을 확인할 수 있다.  

 

테스트를 돌려본다.

 

 

 

 

방금 작성한 기능에 대한 메서드들이 정상적인 동작을 하는 것을 확인할 수 있다!

 

위에서 TDD 방식으로 메서드를 구현해보면서 뭔가 편하지 않았나? 

만약 테스트 코드 없이 해당 내용을 수정하려고 헀다면? 

정상적으로 동작하는지에 대해 전체 코드를 실행시키거나, 

해당 메서드에 대해 중간 중간에 올바르게 동작하는지 System.out.println 혹은 log.info 를 사용했을 것이다. 

 

그러나 현재는 지금 TDD 방식을 따라 해당 기능의 테스트코드가 동작 테스트를 보장해주고 있다. 

 

처음에는 테스트 코드를 먼저 작성하는 것은 번거로운 작업이라고 생각할 수도 있다. 

그런데 어째서 이득을 많이 챙겨간 기분이 들까? 

처음부터 다시 생각해보자. 

 

기능에 대해 테스트 코드를 하나 작성하였다. 

기능이 정상 동작하는지는 구현하고 바로 확인할 수 있었다. 

리펙토링을 하였으나 먼저 작성한 테스트 코드를 통해  다른 메서드들의 동작도 올바름을 확인할 수 있다. 

리펙토링을 하더라도 확인을 위해 테스트 코드는 추가*변경할 필요가 없었다. 

 

 

 

하나의 테스트 코드를 통해 private 메서드를 담은 public 메서드의 바른 동작을 확인할 수 있도록

가치 높은 테스트 코드를 작성했다. 

리펙토링을 하더라도 테스트 코드가 정상 동작했기에 다시 정상 동작을 확인하는 추가 수정 없이,

수정된 코드의 안정성을 빠르게 확보할 수 있었다. 

 


하나의 기능을 구현하는데 TDD를 활용하여 개발해 보았다. 

 

다시 가볍게 살펴보면

테스트 코드는 작성된 프로그램의 안정성과 협업에 큰 도움을 주기에 작성해야하며  

TDD를 사용해 개발한다면 테스트 코드의 이점을 더욱이 취할 수 있으며 효율적인 프로그래밍이 가능하다. 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함