The danger of mock objects을 보면 mock object의 사용에 관한 위험성을 지적하는 내용이 나온다.

Mock object는 잘못쓰면 부작용이 있을 수도 있고 위험할 수도 있다. 그럼 잘못쓰는데도 안 위험한 기술이나 API는 있을까? 당연히 모든 기술은 잘못 쓰면 다 위험하다. 디자인 패턴이 아무리 좋고 프레임워크를 사용하는 것의 장점이 많을지라도 적절한 상황에서 바른 방법으로 사용하지 않으면 다 소용없다.

그런 원론적인 얘기 말고 Cedric이 지적한 케이스를 보자. 어느 Method가 connection을 close하는가에 대해서 테스트하는 것을 Mock connection을 만들어서 close가 호출되는가를 체크하는 식으로 만들면 개발자는 자기 애플리케이션에 문제가 없다는 착각에 빠진다는 것이다. 예를 든 것이 close에 문제가 있는 JDBC드라이버로 업그레이드를 했을때 이 테스트는 여전히 성공했다고 나오기 때문에 그것을 못잡아 낸다는 것이다. 답글을 보니 반쯤은 공감을 하고 있고 반쯤은 반대를 하고 있다.

아주 어이없는 주장이다.

Test, TDD, UnitTest, Mock, Stub등은 항상 많은 논쟁이 있는 분야이다. QA를 통한 수동테스트 방식이 아닌 개발자 스스로 작성하는 방식의 TDD나 자동화된 테스트기반의 다양한 테스트 기법이 나온지 사실 오래 되지도 않았기 때문에 여전히 논쟁이 있을 수 있다.

그 중 Mock Object는 제일 많이 논란의 대상이 되는 것을 볼 수 있다. 얼마전에 TDD뉴스그룹에서  또dynamic mock과 static mock에 대한 논쟁이 있었다. 사실 mock object에 대한 정확한 정의부터 많은 혼란이 있다. mock을 그저 real을 대신하는 가짜라는 의미로 사용하는 것이 가장 일반적이긴 하다. Martin Folwer는  stub과 대비해서 interactive한 테스트가 가능하게 해주는 것이라는 정의를 내리기도 했다. 사실 그 용어가 처음 소개 되었을 때는 기대결과(expectation)와 그에 따른 검증(verify)가 가능한 collaborator라는 의미로 얘기되었던 것 같다(정확한 내용은 찾아보기 귀찮아서.. -_-)

Cedric이 말하는 위험한 mock이란 넓은 의미로 가짜 오브젝트를 말하는 것 같다. 그것을 왜 사용하는가? 단지 테스트하기 쉬워서인가? 그것만은 아니다. Mock을 사용해야 하는 일차적인 이유는 real을 사용하려면 너무 많은 리소스를 사용하게 되고 시간이 걸리고 real을 테스트 환경에서 사용하기가 매우 까다로운 경우기 때문일 것이다. 하지만 그렇지 않은 경우에도 mock의 사용은 필요하다. 그것은 Unit Test가 지향하는 독립적인 테스트(isolated test)를 하기 위함이다. Cedric의 케이스로 다시 돌아가서 보면 내가 테스트 하는 코드는 분명 db코드 수행을 마치고 항상 주어진 connection의 close를 하지만 업그레이드 된 JDBC드라이버가 문제가 있기 때문에 수행을 실패한다면 그것은 JDBC드라이버에 대한 테스트이지 내가 만든 메소드의 테스트는 아니다. JDBC드라이버에 따라서 내 method의 close 수행여부가 결정된다는 것은 우스운 일이다. 테스트는 각각 작성된 목적이 있는 것이다. JDBC드라이버가 못미더워서 그것을 검증하고 싶다면 독자적인 테스트를 작성하면 된다. 내 코드는 이상적인 collaborator(JDBC driver)가 있다는 전제하에 그와 협업을 잘 수행해 내는가만 확인하면 된다. 외부의 리소스와 클래스 등에 독립적인 테스트를 수행하는 것은 Unit Test의 기본이다. 그것을 애플리케이션 전체적인 관점에서 외부리소스나 사용 라이브러리까지 포함한 넓은 의미의 테스트가 아니기 때문에 개발자에게 잘못된 확신을 주어 위험하다는 것은 다양한 테스트수행 목적에 대한 착각에서 비롯된 주장일 뿐이다.

더 살펴보면 그의 주장은 스스로 모순이다. Mock은 real을 사용하기가 아주 어려운 경우만 사용하라고 했다. 그럼 Connection의 real을 가져다가 테스트하기 아주 어려운 경우가 발생했다고 보자 그래서 조심스럽게 mock을 썼다. 그런데 JDBC Driver를 업그레이드하고 보니 close에 문제가 있었다. 그럼 그렇게 주의 깊게 mock을 사용한 테스트는 이때 그런 문제를 잡아줄 수 있는가? 당연히 아니다. 결국 그가 주장하는대로 외부리소스(JDBC Driver)에 대한 검증을 그것을 사용하는 코드의 테스트에서 하려고 한다면 어떠한 경우에도 mock을 사용하면 안될 것이다. 위험하다고 하지말고 절대사용불가라고 해야 할 것이다.

Rod Johnson은 테스트는 thorough해야 한다고 했다. 그러기 위한 가장 기본적인 전략은 다양한 레벨의 테스트를 수행해야 하는 것이다. 자신이 만든 코드가 의도한대로 동작하는지 독립적으로 테스트 해보기 위해서는 mock을 이용한 unit test를 써야 한다. 그것이 외부리소스와 잘 연계해서 동작하는지는 integration test레벨에서 하면 된다. 그것이 복잡한 시나리오를 가진 상황에서 여러모듈이 연계해서도 잘 동작하는지 확인하려면 functional test를 하고 고객의 요구사항을 정확히 반영했는지는 acceptance test를 또 해야 할 것이다. 필요한 성능을 가졌는지는 performance test를 별도로 수행해야 함은 물론이다. HA시스템를 위한 scalability test도  해야한다. 필요에 따라 외부리소스나 사용 라이브러리 자체에 대한 검증 테스트도 필요할 것이다. 이렇게 다양한 접근방법을 가지는 테스트를 통해서 테스트는 완벽해져야 한다.
Mock은 그러한 테스트 중에서 테스트에서 사용하기 힘든 리소스를 써야 하는 경우, 예를 들면 JNDI, MAIL, 또는 isolated test에서 collaborator를 대신하는 목적으로 쓰면 된다. 또 자체적인 검증코드를 가진 interactive한 테스트가 필요한 경우에도. 필요한 경우라면 마음 것 쓰면 된다. 그다지 주의 할 것도 없다. Mock을 쓰는 것이 더 복잡한 테스트를 만드는 경우만 피하면 될 것이다.
Cedric과 같이 mock의 사용에 대해서 불편해하는 경우는 대부분 TDD와 같은 white/glass box test 방법을 사용하지 않기 때문이라고 보인다. 아무리 그래도 mock에 대해서 쓸모없는 비방은 좀 그만 했으면 싶다.

Related posts:

  1. EasyMock 2.1 Released
  2. 켄트 벡의 오리사냥 – Optimistic Typing
  3. SimpleNamingContextBuilder를 이용해서 JNDI를 이용하는 코드 테스트 하기
  4. JSP UnitTest
  5. Rod Johnson의 Testing with Spring
  6. 이클립스에서 JUnit4 테스트의 특정 테스트메소드만 실행하기
  7. Spring기반의 Hibernate DAO Unit Test 만들기

Facebook comments: