본문 바로가기

ComputerScience/Software Engineering

소프트웨어공학 - 19. Test-Driven Development

728x90

1. Unit Testing

- 코드가 옳다는 것을 검증하는 방법이다.

- 유닛 테스트는 divide and conquer 전략을 따른다. 전체 시스템의 유닛들이 잘 동작함을 검증함으로써 전체 시스템의 신뢰를 높인다

- 테스트를 수행하는 코드인 test suite를 만들어서 유닛 테스트를 수행할 수 있다. 

- 테스트 코드를 만드는 일이 오히려 개발시간을 늦추는 일처럼 보일 수 있으나 추후 유지보수와 디버깅이 매우 쉬워지기 때문에 (프로젝트가 클수록) 훨씬 이득이다.

- 테스트 대상인, unit은 class가 될수도 있고 class들로 구성된 모듈이 될 수도 있다. 그 테스트를 수행하는 코드가 unit under test이다.

- 테스트를 수행하도록 unit test code를 호출하는 주체를 driver라고 한다.

- 검증코드가 아직 개발되지 않은 다른 모듈과도 소통을 해야하는 경우 Stub라는 더미 모듈을 만들어서 대체,사용한다.

2. Regression Testing

- unit testing을 통해 검증을 거치면서 개발된 코드들이, 합쳐졌을때도 잘 동작하는지 다시 검사하는 것이다.

- 내가 수정한 코드가 합쳐졌을 때, 다른 부분에 영향을 미치는 경우를 검사할 수 있다.

- 반복적인 작업이기 때문에 test suite을 만들어서 (자동화) 회기 검증을 수행할 수 있다.

3. JUnit

- test suite 작성을 위해 사용되는 java framework이다.

- agile 개발 프로세스에서 특히 Unit testing이 많이 사용된다. Test-Driven Development의 표준 툴이 되었다.

- IMath class에 isqrt함수를 만들었다. 이 함수를 검증하는 JUnit test class를 만들어보자.

- 테스트 클래스를 실행해보면 케이스의 성공/실패 결과를 얻을 수 있다. 

- 이렇게 코드로 만들어 놓으면 매번 반복적으로 검증하기 편리하다. (직접 콘솔 화면에 printf()해서 테스트를 수동으로 하는 것보다 훨씬 낫다)

- JUnit에서는 위처럼 expectation과 결과값을 비교할수 있도록 다양한 함수가 제공된다.

- object를 비교할때 주의할 것이 있다. expected 값과 actual 값의 올바른 비교를 위해 equals를 정의해야한다. (name이 같으면 두 Person object는 같다)

- Junit3의 구버전 이지만 개념은 지금과도 같다. SquareRoot에서 오류가 반드시 발생해서 catch block으로 이동해야 한다. 그러지 못하면 test 실패로 fail을 호출한다.

- Junit4부터는 @Test 접두어를 사용한다.

4. Fixtures

- test case (method)를 실행하기 전 혹은 직후에 다른 코드를 실행하고 싶을 때 활용한다.

- JUnit에서 모든 runTest()는 내부적으로 setUp() -> 테스트 케이스 메서드 수행 -> tearDown()을 순서대로 호출한다.

- 따라서 setUp과 tearDown을 override하면 테스트 케이스 메서드 수행 직전/직후 타 코드 실행이 가능하다.

- Junit4부터는 오버라이드하지 않아도 우측처럼 어노테이션을 사용할 수 있다.

5. Testing Exceptions (Error handling)

- 기대값과 실제값이 같냐를 확인하는 예시는 많이 봤다. 이번에는 에러가 발생했을때 의도대로 잘 처리를 하는지 검증하는 테스트 코드 예시를 살펴본다. 

- 마찬가지로 junit4에서부터는 어노테이션을 활용할 수 있다.

- 기대하는 exception과 실제 발생한 exception이 일치하는지를 확인한다.

6. Timed Test

- 테스트 케이스 메서드가 timeout내에 실행되지 않으면 실패로 간주한다.

7.  Examples

- 참고로 test driver에 의해서 반드시 testSetX()와 testSetY()가 순서대로 호출되지는 않는다.

- setUp, tearDown은 test case method 시작 전/후에 동작하는 함수라고 했다.

- 아래처럼 모든 test case method들이 실행되기 전/후에 동작하는 메서드도 정의할 수 있다.

- 보통은 DB와 연결/해체 같이 딱 한번씩 전후로 수행될 필요있는 작업을 위해 사용한다.

- 스택 클래스를 검증하는 테스트 코드를 살펴보자.

- 그런데 testRepOk 처럼 너무 복잡하게 전부다 검사하는 것은 좋지 않다.

- 이렇게 바로 알아볼 수 있도록 잘게 쪼개는 것이 좋다.

- 이런 테스트 케이스 (메서드)들이 모이면 suite가 된다.

8. Test driven development

- 이 개발 프로세스에서는 프로그램을 만들기 전에 테스트 코드를 먼저 만든다.

- 그 다음 테스트를 통과하기 위해 코드를 만든다.

- 테스트를 통과할때까지 개발, 테스트를 반복한다. (agile 개발에서는 거의 표준으로 사용되는 방법이다)

- 예를들어 어떤 서비스를 대규모 업데이트 하였는데 이전 기능들이 다 지금도 잘 동작하는지 확인해야 한다면, TDD는 빛을 발한다.

9. 정리

- unit test에 용이한 설계는 결국 최종적으로 꽤 좋은 전체 시스템 설계를 유도하게 된다.

728x90
반응형