프로그램을 개발 할 때 만든 코드에 대한 검증이 필요할 때가 있습니다. 프로그래머가 사람은 누구나 실수를 할 수 있고 아무리 완벽하게 코드를 짰다고 생각 할지라도 예상치 못한 사용으로 로직이 틀릴 수도 있기에 이를 검증할 수 있는 도구가 필요로 했습니다. 그래서 테스트 코드라는 코드를 검증하기 위한 코드를 개발하는 방법론(TDD)이 제시되었고, 자바에서는 Junit이나 AssertJ와 같은 라이브러리를 통해 쉽게 코드를 검증할 수 있는 환경 또한 제공하고 있습니다.
하지만 코드의 검증 범위에 따라 어디까지 테스트를 하고 어떠한 가능을 제공하는 라이브러리를 적절하게 쓰고 어떻게 가독성있는 테스트 코드를 짤 지 고민을 해야 합니다. 그래서 Mockito를 사용하기에 앞서 먼저 프로그래밍 테스트에 대한 고찰이 필요하면서 Mockito를 어떻게 하면 잘 사용하여 클린한 코드와 검증 가능한 테스트에 대한 고민을 해야 됩니다. 이런 고민까지 하게 되면 설계부터 테스트 코드까지 적절한 Mockito를 사용하여 코드의 결함을 줄이고 가독성있게 하여 코드를 좀 더 클린하게 만들 수 있다고 생각합니다.
보통 정의되는 통합 테스트는 독립적으로 개발된 소프트웨어 단위가 서로 연결될 때 올바르게 작동하는지 확인하는 것을 의미하고, 단위 테스트는 개별적인 소프트웨어 단위를 테스트하는 것을 의미합니다. 하지만 단위 테스트에서 이는 사람마다 다르게 정의 될 수 있습니다. 단위를 클래스의 메서드 단위로 보는 사람도 있을 뿐더러, 클래스 단위 혹은 행위 자체를 단위로 보는 의견 또한 있습니다.
단위 테스트는 순수하게 로직만 검증하는 코드로 만들 수 있습니다. Junit을 통해서 데이터를 넣어 값이 제대로 확인만 하면 되니까요. 하지만 단위테스트를 작성하려면, 비즈니스 로직을 파악하여 단위가 무엇인가, 내가 정말 테스트하고자 하는게 무엇인가 알아야 하고 어디까지 테스트를 해야 할 지 염두해야 합니다.
그렇다면 단위테스트의 단위는 어디까지 측정할 수 있을까요? Wikipedia에 따르면 단위는 "전체 모듈", "개별 함수", "전체 인터페이스/클래스"일 수도 있고 "개별 메서드"일 수도 있습니다. 또한 어떤 사람의 경우 프로덕션 코드가 변경되지 않는 한 결과가 일관된다고도 말을 합니다. 또 어떤 사람은 행위 자체를 "unit"을 정의하기도 하죠.
마틴 파울러의 단위테스트 정의에 따르면 아래와 같습니다.
하지만 복잡한 대규모 어플리케이션을 만든다고 하면, 단위 테스트는 정의하기 어렵습니다.
예를들어 수학의 작은 연산 단위인 더하기, 빼기, 나누기, 곱하기를 단위 테스트로 만들 수 있지요.
하지만 이 작은 단위를 가지고... 원의 넓이를 정의할 수 있고.. 더 나아가...
따라서 아래와 같이 마틴파울러는 단위 테스트를 고립되거나 혹은 연결된 테스트를 의미한다고 말을 합니다.
서로 연결된 서비스들이 있다고 가정하면, 테스트할 서비스 로직 이외의 모든 것이 올바르게 작동한다고 가정하고 테스트를 작성합니다. 테스트할 메서드는 제외한 연결되어있는 메서드는 올바르게 작동한다고 가정하고 테스트 코드를 작성합니다.
그래서 Unit Test에서 각각의 Test Suite은 무엇을 테스트할 것인지 그 대상을 명확히 해야 합니다. 이때 하나의 테스트에서 테스트하고자 하는 주요 대상이 되는 Unit을 SUT(System Under Test)라고 부르는 데, SUT는 용어 그대로 시스템의 일부이기 때문에 다른 Unit과 상호 작용을 할 수 있습니다. 그 중 SUT는 자신이 의존하고 있는 객체가 있을 수 있는데, 이를 DOC(Depended On Component)라고 부릅니다.
따라서 단위 테스트는 내가 테스트하고자 하는 대상인 sut(System Under Test)를 고립시켜 테스트 대상이 행위할 수 있는것 자체에 집중할 수 있어야 하고 통합 테스트와 다르게 doc(Depended On Component)에 대해선 테스트를 위한 대역 배우(Test Double)가 필요합니다.
xUnit Test Patterns의 저자인 제라드 메스자로스(Gerard Meszaros)가 만든 용어로 테스트를 진행하기 어려운 경우 이를 대신해 테스트를 진행할 수 있도록 만들어주는 객체를 말합니다.
Test Dobule은 영화 촬영 시 위험한 역할을 대신하는 스턴트 더블(= stunt man)에서 파생되었다.
몇몇 코드의 경우 복잡한 계층레이어를 띄고 있을때, 데이터베이스나 파일 시스템과 같은 외부 리소스와의 모든 협업에서 test double을 사용해야 한다고 주장합니다.
따라서 Mockito는 Mock을 생성하고 관리하기 위한 프레임 워크입니다.
그래서 Mockito를 사용 시기는 제어할 수 없는 영역을 대체하기 위해 사용합니다. 예를들어 DB에 의존적인 dao나 repository를 사용할 때 그리고 외부 API와 통신할 때 Mocking을 사용합니다. 또한 복잡한 레이어 구성을 가질 때 사용하는 데 예를 들어 spring service안에 무수히 많은 service가 존재할 수 있습니다. 이런 복잡한 레이어 계층을 가지고 있으면 Mockito를 사용하면 집중할 수 있는 service에만 테스트를 하고 그 외에는 mock을 두어 테스트 대상에서 제외할 수 있다는 이점이 있습니다.
Mockito가 많은 기능을 제공하지만 무작정 도입하는 건 아니라고 생각됩니다. Mockito를 적용하기 전에, 테스트 할 대상이 무엇인지 정확하게 파악하고 충분히 고민한다면 더 좋은 테스트 코드를 작성할 수 있다고 생각합니다.
만약 Mockito를 사용하게 된다면, 이처럼 테스트하려는 대상의 내부 구현도 일부 알고 있어야 합니다. Mock 대상 객체가 많아질수록, 호출하는 메소드가 많아질수록 테스트는 길어질 것이기에 테스트의 가독성을 저하시키고 되려 테스트 대상에 집중하는 것을 방해할 수 있기에 내부 설계에 따라 적용을 해야합니다.
그리고 고립된(Solitary)서비스의 경우 POJO로 로직을 분리하여, 연결된 모듈들을 모두 POJO의 로직을 사용하면 테스트코드를 가독성있게 만들기 쉽다고 생각합니다.
물론 가독성이 떨어뜨리는 테스트 코드를 작성한다면 애초에 설계부터 잘못된 것이니 설계부터 다시 뜯어고칠 수 있지만 기존 리거시의 대한 코드도 염두해야 합니다. 그래서 Mockito를 사용한다면 적절한 상황에서 써야됨을 알 수 있습니다.