Mock Object의 위험성?
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에 대해서 쓸모없는 비방은 좀 그만 했으면 싶다.
Mock objects can give you a deceptive sense of confidence 여기까진 좋아요. 그런 위험이 있다고 경고해 줌으로써 테스트에 대해 더 많은 생각을 할 수 있으니까.
and that’s why you should avoid them unless there is really no alternative.
하지만 결론에 동의하기는 어렵네요. Developer Test에서 Mock의 장점은 쉽게 이해할 수 있을텐데, 이 사람은 어떤 alternative를 얘기하는 건지? 설마 Stub을 말하는 건 아닐테구. Stub을 포함한 개념의 Mock없이 자주 돌려볼 수 있을 만큼의 속도가 보장되는 자동화된 테스트의 작성이 가능하다고 믿는 걸까요?
물개선생/ TDD를 안하고 Test Last방식을 쓴다면 테스트를 자주 돌릴 필요도 없을 것이고 수행 속도도 별로 상관안하겠지. Cedric은 TestNG의 개발자인데 이 글을 읽고 나니 TestNG에 더 정이 안가네.
어떤 이유에 의해서 상황이 바뀌어 원래 의도했던 mock의 기능을 제대로 수행하지 못하는 경우인데, 이건 mock 자체의 잘못이 아니라 상황과 시간의 변화에 따라 변경에 대한 통제영역을 제대로 관리 하지 못한데서 나오는 위험성을 지적한 것 같습니다. 물개선생님이 의문을 제기하신 alternative는 mock이나 mock에 대응하는 동등수준의 대안적인 기술을 의미한다기 보다는 테스트체계 유지를 위한 프로세스와 사람, 관리의 영역으로 확장하여 생각해 보는것도 좋은 어프로치라는 의미 아닐까 싶네요.. ^^
cbiscuit/ Cedric이 주장하는 것은 mock이 아닌 real의 오류는 mock을 쓴 테스트로는 찾을 수 없다는 것입니다. 그것은 그 real에 대한 검증과 테스트를 안한 것에서 기인하거나 real을 포함한 integration test를 소홀히 해서 발생하는 것이지 mock을 쓴 것이 문제의 원인이라고 말하기는 힘들다고 봅니다.
이메일서버가 결함이 있을 수 있으니 수백만통의 email전송 및 고객의 답장처리 기능을 모두 real시스템과 결합해서 test를 하라는 주장은 말이 되지 않는 것이죠. 그것이 email mock을 이용하는 것이 위험하다는 주장이 될 수는 없다고 봅니다. Isolated test의 필요성과 장점에 대한 근본적인 부정이기 때문에 Cedric의 주장은 동의하기가 힘듭니다. Mock을 대신할 만한 다른 alternative가 있다는 것도 마찬가지고요.
글쎄요.. Cedric이 주장하는 것은 real이 변경되었을때 mock이 이 변경에 대한 반영을 하지 않으면 위험하다는 것 같구요. Toby님 께서 지적하신 정도의 주장은 하지 않은 것으로 보입니다.
그 밑에 코멘트에서 Erik이란 사람이 Integration Test에서 mock은 좋지 않다라고 했는데, 거기에 대해서 Cedric은 “Big Picture를 놓치고 있다. Unit Test든 Integration Test든 애플리케이션이 제대로 동작하는지 알아보기 위한 코드로 작성되어야 한다”라는 친절한 코멘트를 붙여 놓았습니다.
TestNG라는 꽤 괜찮은 도구를 개발한 사람이 Toby님이 지적한 대로 그렇게 편협한 시각을 가진 사람이라고 보긴 힘드네요. ^^