이동식 저장소

Hilt Test Principles 본문

Primary/Android

Hilt Test Principles

해스끼 2022. 6. 30. 09:22

 

다음 문서를 요약한 글입니다. 인사이트 넘치는 글이니 일독을 권합니다.

 

Hilt Testing Philosophy

Overview This page aims to explain the testing practices that Hilt is built upon. A lot of the APIs and functionality in Hilt (and certain lack of functionality as well) were created on an unstated philosophy of what makes a good test. The notion of a good

dagger.dev


좋은 테스트 코드는 객체 내부의 구현에 독립적이어야 한다. 예를 들어 함수 A를 실행했을 때  함수 B가 실행되었는지 테스트하는데, 함수의 이름이 바뀌면 테스트가 실패할 수 있다. 따라서 내부 구현에 신경쓰지 말고, 함수가 반환하는 값만 신경쓰는 편이 이롭다. 이런 테스트 기법을 블랙박스 테스트라고 한다.

 

또, 테스트 코드는 실제로 객체를 사용하는 것처럼 작성돼야 한다. 단순히 커버리지만 넓히는 테스트에 무슨 의미가 있겠는가? 개별 함수의 동작을 검증하는 테스트도 필요하고, 여러 함수를 조합한 use case 역시 테스트돼야 한다.


Hilt는 모든 클래스를 테스트해야 한다던가 하는 엄격한 규칙을 강요하지는 않는다. 사실 모든 클래스를 테스트하면 사용자의 관점에서 테스트해야 한다는 원칙을 등한시하게 될 수도 있다.

 

어쨌든, 테스트를 작성하려면 우선 테스트할 객체를 만들어야 한다. 어떻게 만들 것인가?

  1. 실제 코드에서처럼 객체를 만든다.
  2. 라이브러리에서 제공하는 공식 fake를 사용한다.
  3. 최후의 수단으로 mock을 사용한다.

실제 코드처럼?

왜 실제 코드처럼 객체를 만들어야 하는가? 그래야 테스트의 의미를 살릴 수 있기 때문이다. 가장 좋은 테스트는 실제 사용 예시를 검증하는 테스트이다. 따라서 테스트할 객체 역시 실제처럼 만들어야 한다.

 

다행히도 우리는 Hilt를 사용하여 객체를 inject할 수 있다. 그런데 만약 Hilt가 없다면 어떻게 해야 할까? 예를 들어 Hilt의 기반 플랫폼인 Dagger만 사용해야 한다면?

 

음.. 그냥 생성자로 만들면 되지 않을까?

Android Developers

그런데 만들고자 하는 객체의 의존성이 복잡하다면 어떨까? 나는 ``A``만 만들고 싶었는데, 알고 보니 ``B``, ``C``, ``D``까지 만들어야 하는 상황이고, 의존성이 기하급수로 늘어나 감당할 수 없게 될 것이다.

 

객체를 여러 개 만들어야 하는 Integration test라면... 상황이 악화되고 있습니다!!

Mock

객체를 만드는 boilerplate가 많아질수록 mock의 유혹이 다가온다. Mock은 특정한 결과를 반환하도록 정의된 객체이다. 그런데 mock은 주로 테스트에서 정의된다. 그 말인 즉슨 테스트 작성자가 원하는 결과만을 반환한다는 뜻이다.

그게 왜요? 테스트 통과하기만 하면 되잖아요?

테스트는 코드의 작동을 올바르게 검증하기 위해 존재하며, 그렇기에 실패한 테스트 역시 가치를 인정받는 것이다. 단순히 성공하기만 하는 테스트는 사실 큰 의미가 없다.

 

어쨌든, 위에서 말한 이유 때문에 mock은 최후의 수단으로만 사용해야 한다. 차라리 잘 ㄱ현된 fake 객체를 사용하자.

결론

  1. 테스트는 실제 객체의 사용법을 검증해야 한다.
  2. 따라서 객체를 만들 때도 실제 코드처럼 만들어야 한다.
  3. Dagger로는 테스트 코드에서 객체를 만들기 너무 어려웠기 때문에, Hilt는 테스트에서도 쉽게 사용할 수 있도록 구현되었다.

훌륭하다. 그쵸?

Comments