-Unit Test(70%) , Interation Test(20%) End to end Test(10%) 정도의 테스트 비율로 테스틀 하는 것이 좋다고 구글에서 권장하고 있다. 또한, 테스트의 종류를 나누는 것에 정답은 없으므로 현재 내 앱의 코드 상황에 따라 달리하면 된다. 반드시 7 : 2 : 1의 비율로 테스트를 진행 할 필요는 없다.
UnitTest
-코드의 가장 작은 단위 , 예를 들면 함수 등을 독립적으로 검증하는 테스트를 말한다.
-특정 모듈(기능)이 의도대로 정확히 작동하는지 확인하는 테스트를 말한다.
-빠르고 자주 실행 할 수 있어서 개발 초기에 문제를 발견하고 수정하는 데 도움이 된다.
Interation Test
-여러 모듈 또는 서비스가 함께 작동할 때 상호 작용하는 부분을 테스트한다.
End to end Test
-실제 사용자 시나리오를 기반으로 테스트한다. 즉, 사용자와의 상호작용해서 발생 하는 것들을 테스트한다.
-로컬 테스트란 , 기본적으로 JVM 위에서 동작하는(실제 디바이스 또는 에뮬레이터에서 동작하는 것이 아니라 현재 JUnit Test만 돌릴 수 있는 환경위에서 돌아간다는 의미) 경우 장점은 시뮬레이터 또는 실기기에서 테스트를 한다면 리소스를 등록하고 다시 해제하는 등의 작업에서 발생하는 오버웨이트 문제점이 JVM위에서는 발생하지 않는다는 것이다.
스몰 로컬테스트 : 작은 함수 단위 또는 아주 작은 메서드와 연계되는 작은 테스트들을 테스트 하는 것
빅 로컬테스트 : 여러 개의 인터랙션을 통해서 일어나는 여러 클래스들의 상호작용을 통해서 하나의 테스트를 위한 하나의 케이스를 테스트 하는 것
-Instrumented Test란, 에뮬레이터 또는 실제 안드로이드 기기에서 테스트를 실행하는 것으로 Espresso 라이브러리를 사용한다.
-안전성 / 호환성 테스트는 반드시 실제 안드로이드 기기에서 진행하여야 한다. 하지만, 단위 테스트 같은 경우는 가능한 JVM에서 실행해야 한다.
-테스트는 실제 발생 할 수 있는 에러를 예방할 수 있게 하기 위한 것이기 때문에 의미 없는 테스트를 막 추가하지 말고 의미있는 테스트를 하여야 한다.
-Edge cases를 통해 좋은 테스트 설계를 할 수 있다.
Boundary condition : 입력 값이 범위에 해당 할 때 입력 값 범위 밖에서 발생하는 에러를 찾을 수 있도록 테스트를 해야한다.
네트워크 에러 : 미리 네트워크 테스트를 안해서 나중에 문제가 생기는 경우가 많은데 , 404 또는 500이 뜨는 에러가 발생할 수 있으므로 대처하는 방안을 마련할 수 있도록 해야한다.
잘못된 데이터 : rest api를 통해서 데이터를 받았을 때 json 데이터 포맷이 깨져서 나오는 경우도 있는데 이런 데이터 타입에 관련된 테스트를 해야한다.
중요한 객체가 사라졌다가 다시 생성되는 것도 가정해서 테스트 해봐야 한다. 예를 들어 화면의 configuration 또는 다크모드 밝음모드 변경 등을 말한다.
-테스트 구현에 익숙하지 않다면 처음에 시작 할 수 있는 것은 두가지로 경우에 학습해볼 수 있다.
-독립함수 , 다른 곳과 의존성이 무조건 없는 코드에 대한 JVM 단위 테스트
-핵심 시나리오에 대해서 E2E Test를 하나 만들고 해당 테스트에 들어가는 요소들을 잘게 쪼개보는 것이다.
-예를들면 , 로그인을 하는 하나의 유스케이스가 있을 때 로그인에 대한 올바른 개인정보를 넣은 후 서브 미션을 눌렀다고 하면 성공이 나올텐데 거기서 수행되는 작어비 서버로 보내는 것이 있을 수 도 있고 , 성공했을 때 로컬데이터에 어떻게 저장하는가가 있을 수 도 있고 , 이런 것들을 테스트 해보는 것이다.
-실제/의사 디버깅 과정에서 테스트 코드를 적용해보는 것이다.
-예를 들어 서버에 무언가를 보내는 것을 테스트 한다고 가정했을 때 사용해볼 수 있는 방법이 3가지가 있다.
-예를들어 실제로 테스트 케이스 내부에서 어떤 호출을 해서 실제로 서버로 보내서 받아서 결과를 확인해보는 것이다.
-페이크란 실제와 거의 동일한 역할을 하는 것을 말한다. 예를들어 테스트 케이스 내부에서 실제로 서버에 보내는 것을 메서도의 호출을 통해서 보내는 것이 아니라 정해진 데이터를 받아 적어서 서버로 보내보는 것이다. 이렇게 하면 실제 코드를 작서하는 것에 준하는 신뢰성을 갖고 있다. 물론 결과물이 진짜 결과물은 아니다.
-실제 객체의 행위를 모방한 테스트를 위한 객체를 만들어서 사용하면되는데 , 이는 실제 로직을 수행하는 대신 미리 정해진 답을 반환한다. 이 가상의 객체는 테스트 하려는 코드와 상호작용하는 객체이다. 즉 가상의 객체를 사용하여서 외부 시스템이 필요 없게 만들어서 테스트를 격리시켜서 활요하는 것이다.
-테스트라고 하는 것은 실제로 일어나는 것을 그대로 반영해야 한다. 즉, 테스트가 실패하면 실제로도 해당 코드의 실행이 실패해야 되고 , 테스트가 성공하면 실제로도 해당 코드의 실행이 성공해야 된다. 만약 테스트를 잘 못 만든다면 테스트 대상 코드에 문제가 없는데도 실패하거나 테스트 대상 코드에 문제가 있는데도 성공하는 불상사가 발생한다. 따라서 이러한 일들을 예방하기 위해서 실제코드를 사용해서 테스트를 진행하는 것이 좋다. 그러나 반드시 100% 무조건 실제코드를 사용하면 안되고 웬만하면 실제코드를 사용해서 테스트 하는 것이 좋다. 왜냐하면 네트워크 같은 경우는 어쩔 수 없는데 왜냐면 네트워크를 테스트 케이스에서 활용하면 의존성이 외부에 생겨서 만약에 네트워크에서 문제가 생기면 해당 테스트 코드에 이상이 없는데도 실패가 나올 수 있기 때문이다.
-type-safe한 matcher를 적극 활용하기 : hamcrest , truth 등등 + built-in matcher
-interaction 보다 state를 체크하기
-shared code를 적절히 사용하기
-복잡한 경우에는 mocking 하지말기(특히 Context)