Android
JUnit을 이용한 안드로이드의 단위 테스트
수색…
비고
- Vogella : JUnit을 이용한 유닛 테스트
- Junit 주석 : java2novice.com
- Assert 클래스 : junit.org
- JUnit Api : tutorialspoint.com
- Medium.com 게시물을 테스트하는 Anroid
로컬 단위 테스트 만들기
테스트 클래스는 /src/test/<pkg_name>/
예제 테스트 클래스
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
int a=4, b=5, c;
c = a + b;
assertEquals(9, c); // This test passes
assertEquals(10, c); //Test fails
}
}
고장
public class ExampleUnitTest {
...
}
테스트 클래스에서는 여러 테스트 클래스를 만들어 테스트 패키지 안에 배치 할 수 있습니다.
@Test
public void addition_isCorrect() {
...
}
테스트 메소드는 여러 테스트 메소드를 테스트 클래스 내에 생성 할 수 있습니다.
주석 @Test
주목하십시오.
Test 주석은 JUnit에게 그것이 첨부 된 public void 메소드가 테스트 케이스로 실행될 수 있음을 알린다.
같은 몇 가지 다른 유용한 주석이 있습니다 @Before
, @After
등 이 페이지는 시작하기에 좋은 장소가 될 것입니다.
assertEquals(9, c); // This test passes
assertEquals(10, c); //Test fails
이러한 메서드는 Assert
클래스의 멤버입니다. 다른 유용한 메소드는 assertFalse()
, assertNotNull()
, assertTrue
등입니다. 여기에 정교한 설명이 있습니다.
JUnit 테스트에 대한 주석 정보 :
@Test : Test 주석은 JUnit에 첨부 된 public void 메소드가 테스트 케이스로 실행될 수 있음을 JUnit에 알립니다. 메소드를 실행하기 위해 JUnit은 먼저 클래스의 새로운 인스턴스를 생성 한 다음 주석이 달린 메소드를 호출합니다.
@Before : 테스트를 작성할 때 여러 테스트가 실행되기 전에 유사한 객체가 필요하다는 것을 발견하는 것이 일반적입니다. 와 공공 무효 방법 주석 @Before
방법은 시험 방법 전에 실행하는 것이됩니다.
@After : Before 메소드에서 외부 리소스를 할당하는 경우 테스트가 실행 된 후에 외부 리소스를 해제해야합니다. @After
하여 공용 void 메서드에 주석을 @After
하면 해당 메서드가 Test 메서드 다음에 실행됩니다. Before 또는 Test 메서드가 예외를 throw하더라도 모든 @After
메서드가 실행되도록 보장됩니다.
팁 Android Studio에서 테스트 클래스를 신속하게 생성하십시오.
- 테스트 클래스를 작성하려는 클래스 이름에 커서를 놓으십시오.
- Alt + Enter를 누르십시오 (Windows).
- Create Test를 선택하고 Return 키를 누릅니다.
- 테스트 메소드를 만들 메소드를 선택하고 확인을 클릭하십시오.
- 테스트 클래스를 작성할 디렉토리를 선택하십시오.
- 당신이 끝났어, 당신이 얻는 것은 당신의 첫 번째 시험입니다.
팁 Android Studio에서 테스트를 쉽게 실행할 수 있습니다.
- 패키지를 마우스 오른쪽 버튼으로 클릭하여 테스트합니다.
- 실행 선택 테스트 ...
- 패키지의 모든 테스트가 즉시 실행됩니다.
Android 구성 요소에서 비즈니스 로직으로 이동
로컬 JVM 유닛 테스트의 많은 가치는 애플리케이션을 설계하는 방법에서 비롯됩니다. Android 구성 요소에서 비즈니스 로직을 분리 할 수있는 방식으로 디자인해야합니다. 다음은 Model-View-Presenter 패턴을 사용하는 방법의 예입니다. 사용자 이름과 비밀번호 만 사용하는 기본 가입 화면을 구현하여이를 연습 할 수 있습니다. Android 앱은 사용자가 제공 한 사용자 이름이 비어 있지 않으며 비밀번호가 8 자 이상이고 하나 이상의 숫자가 포함되어 있는지 확인합니다. 사용자 이름 / 비밀번호가 유효하면 우리는 가입 API 호출을 수행합니다. 그렇지 않으면 오류 메시지가 표시됩니다.
비즈니스 로직이 Android 구성 요소와 매우 결합 된 예입니다.
public class LoginActivity extends Activity{
...
private void onSubmitButtonClicked(){
String username = findViewById(R.id.username).getText().toString();
String password = findViewById(R.id.password).getText().toString();
boolean isUsernameValid = username != null && username.trim().length() != 0;
boolean isPasswordValid = password != null && password.trim().length() >= 8 && password.matches(".*\\d+.*");
if(isUsernameValid && isPasswordValid){
performSignUpApiCall(username, password);
} else {
displayInvalidCredentialsErrorMessage();
}
}
}
비즈니스 로직이 Android 구성 요소에서 분리되는 예입니다.
여기서 우리는 다양한 클래스 사이의 다양한 상호 작용을 수용 할 단일 클래스 인 LoginContract를 정의합니다.
public interface LoginContract {
public interface View {
performSignUpApiCall(String username, String password);
displayInvalidCredentialsErrorMessage();
}
public interface Presenter {
void validateUserCredentials(String username, String password);
}
}
우리의 LoginActivity는 대부분 사용자의 가입 양식 (비즈니스 로직)을 검증하는 방법을 알아야하는 책임을 제외하고는 대부분 동일합니다. 이제 LoginActivity는 새 LoginPresenter를 사용하여 유효성 검사를 수행합니다.
public class LoginActivity extends Activity implements LoginContract.View{
private LoginContract.Presenter presenter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = new LoginPresenter(this);
....
}
...
private void onSubmitButtonClicked(){
String username = findViewById(R.id.username).getText().toString();
String password = findViewById(R.id.password).getText().toString();
presenter.validateUserCredentials(username, password);
}
...
}
이제 비즈니스 로직이 새로운 LoginPresenter 클래스에 상주하게됩니다.
public class LoginPresenter implements LoginContract.Presenter{
private LoginContract.View view;
public LoginPresenter(LoginContract.View view){
this.view = view;
}
public void validateUserCredentials(String username, String password){
boolean isUsernameValid = username != null && username.trim().length() != 0;
boolean isPasswordValid = password != null && password.trim().length() >= 8 && password.matches(".*\\d+.*");
if(isUsernameValid && isPasswordValid){
view.performSignUpApiCall(username, password);
} else {
view.displayInvalidCredentialsErrorMessage();
}
}
}
이제 새로운 LoginPresenter 클래스에 대해 로컬 JVM 유닛 테스트를 만들 수 있습니다.
public class LoginPresenterTest {
@Mock
LoginContract.View view;
private LoginPresenter presenter;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
presenter = new LoginPresenter(view);
}
@Test
public void test_validateUserCredentials_userDidNotEnterUsername_displayErrorMessage() throws Exception {
String username = "";
String password = "kingslayer1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredFourLettersAndOneDigitPassword_displayErrorMessage() throws Exception {
String username = "Jaime Lanninster";
String password = "king1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredNineLettersButNoDigitsPassword_displayErrorMessage() throws Exception {
String username = "Jaime Lanninster";
String password = "kingslayer";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredNineLettersButOneDigitPassword_performApiCallToSignUpUser() throws Exception {
String username = "Jaime Lanninster";
String password = "kingslayer1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view).performSignUpApiCall(username, password);
}
}
보시다시피, 우리는 LoginActivity에서 비즈니스 로직을 추출하여 LoginPresenter POJO 에 배치했습니다. 이제 비즈니스 로직에 대해 로컬 JVM 유닛 테스트를 생성 할 수 있습니다.
우리가 하나의 책임, 추가 수업 등을 갖는 각각의 클래스를 고수하는 것과 같은 아키텍처 변화에서 비롯된 여러 가지 다른 의미가 있음을 알아야합니다. 이것은 내가 이것을 수행하는 방법의 부작용 일뿐입니다. MVP 스타일을 통한 디커플링. MVP는이 문제를 해결하는 한 가지 방법 일 뿐이지 만 MVVM 과 같은 다른 대안을 찾아 볼 수 있습니다. 당신은 단지 당신을 위해 작동하는 최고의 시스템을 선택해야합니다.
JUnit 시작하기
설정
JUnit을 사용하여 Android 프로젝트의 단위 테스트를 시작하려면 프로젝트에 JUnit 종속성을 추가해야하며 단위 테스트를위한 소스 코드가 포함될 테스트 소스 세트를 만들어야합니다. Android Studio로 만든 프로젝트에는 이미 JUnit 종속성과 테스트 소스 세트가 포함되어 있습니다.
종속 관계 내에서 모듈 build.gradle
파일에 다음 행을 추가하십시오. Closure
:
testCompile 'junit:junit:4.12'
JUnit 테스트 클래스의 이름은 특별한 소스 세트에있는 test
. 이 원본 세트가 없으면 직접 새 폴더를 만들어야합니다. 기본 Android Studio (Gradle 기반) 프로젝트의 폴더 구조는 다음과 같습니다.
<project-root-folder>
/app (module root folder)
/build
/libs
/src
/main (source code)
/test (unit test source code)
/androidTest (instrumentation test source code)
build.gradle (module gradle file)
/build
/gradle
build.gradle (project gradle file)
gradle.properties
gradlew
gradlew.bat
local.properties
settings.gradle (gradle settings)
프로젝트에 /app/src/test
폴더가 없으면 직접 만들어야합니다. test
폴더 내에서 java
폴더가 필요합니다 (존재하지 않는 경우 생성). test
소스 세트의 java 폴더에는 main
소스 세트와 동일한 패키지 구조가 있어야합니다.
프로젝트 구조 (Android Studio의 Android보기)가 올바르게 설정되면 다음과 같이 표시됩니다.
참고 : androidTest
소스 세트가 반드시 필요한 것은 아니며,이 소스 세트는 Android Studio에서 만든 프로젝트에 있으며 참조 용으로 여기에 포함되어 있습니다.
테스트 작성하기
test
소스 세트 내에 새 클래스를 작성하십시오.
프로젝트보기에서 테스트 소스 세트를 마우스 오른쪽 단추로 클릭하고New
>Java class
선택한다.
가장 많이 사용되는 이름 지정 패턴은Test
추가 할 테스트의 클래스 이름을 사용하는 것입니다. 그래서StringUtilities
된다StringUtilitiesTest
.@RunWith
주석 추가
JUnit이 테스트 클래스에서 정의 할 테스트를 실행하게하려면@RunWith
주석이 필요하다. 기본 JUnit 러너 (JUnit 4 용)는BlockJUnit4ClassRunner
이지만 직접 실행하는 대신 기본 JUnit 러너의 단축형 인 별칭JUnit4
를 사용하는 것이 더 편리합니다.@RunWith(JUnit4.class) public class StringUtilitiesTest { }
테스트 만들기
단위 테스트는 본질적으로 단지 대부분의 경우 실행되면 실패하지 않는 방법입니다. 즉, 예외를 throw해서는 안됩니다. 테스트 메소드에서 특정 조건이 충족되는지 확인하는 어설 션을 거의 항상 찾을 수 있습니다. 어설 션이 실패하면 메소드 / 테스트를 실패하게하는 예외가 발생합니다. 테스트 메소드에는 항상@Test
주석이 주석으로 지정됩니다. 이 주석이 없으면 JUnit은 자동으로 테스트를 실행하지 않습니다.@RunWith(JUnit4.class) public class StringUtilitiesTest { @Test public void addition_isCorrect() throws Exception { assertEquals("Hello JUnit", "Hello" + " " + "JUnit"); } }
참고 : 표준 Java 메소드 명명 규칙과 달리 테스트 메소드 이름에는 밑줄이 포함되는 경우가 많습니다.
테스트 실행하기
방법
단일 테스트 메소드를 실행하려면 메소드를 마우스 오른쪽 버튼으로 클릭하고Run 'addition_isCorrect()'
클릭하거나 키보드 단축키ctrl+shift+f10
.모든 것이 올바르게 설정되면 JUnit은 메소드 실행을 시작하고 Android Studio에서 다음 인터페이스를 볼 수 있습니다.
수업
프로젝트보기에서 클래스를 마우스 오른쪽 단추로 클릭하고Run 'StringUtilitiesTest '
클릭하거나 프로젝트보기에서 클래스를 선택한 경우 키보드 단축키ctrl+shift+f10
사용하여 단일 클래스에 정의 된 모든 테스트를 실행할 수도 있습니다.패키지 (모든 것)
프로젝트 또는 패키지에 정의 된 모든 테스트를 실행하지 않으려면 단일 클래스에서 정의 된 모든 테스트를 실행하는 것처럼 패키지를 마우스 오른쪽 단추로 클릭하고Run ...
클릭하면됩니다.
예외
JUnit은 메서드가 주어진 입력에 대해 특정 예외를 throw하는지 테스트하는데도 사용할 수 있습니다.
이 예제에서 부울 형식 (입력)이 인식 / 알 수없는 경우 다음 메서드가 실제로 예외를 throw하는지 테스트합니다.
public static boolean parseBoolean(@NonNull String raw) throws IllegalArgumentException{
raw = raw.toLowerCase().trim();
switch (raw) {
case "t": case "yes": case "1": case "true":
return true;
case "f": case "no": case "0": case "false":
return false;
default:
throw new IllegalArgumentException("Unknown boolean format: " + raw);
}
}
expected
매개 변수를 @Test
주석에 추가하여 던져 질 것으로 예상되는 예외를 정의 할 수 있습니다. 이 예외가 발생하지 않으면 유닛 테스트는 실패하고 예외가 실제로 발생하면 성공합니다.
@Test(expected = IllegalArgumentException.class)
public void parseBoolean_parsesInvalidFormat_throwsException(){
StringUtilities.parseBoolean("Hello JUnit");
}
이것은 잘 작동하지만 메서드 내에서 단 하나의 테스트 케이스로 제한됩니다. 때로는 단일 메소드 내에서 여러 사례를 테스트하려고 할 수 있습니다. 이 제한을 극복하기 위해 자주 사용되는 기술은 try-catch
블록과 Assert.fail()
메서드를 사용하는 것입니다.
@Test
public void parseBoolean_parsesInvalidFormats_throwsException(){
try {
StringUtilities.parseBoolean("Hello!");
fail("Expected IllegalArgumentException");
} catch(IllegalArgumentException e){
}
try {
StringUtilities.parseBoolean("JUnit!");
fail("Expected IllegalArgumentException");
} catch(IllegalArgumentException e){
}
}
참고 : 일부 사람들은 단위 테스트 내에서 하나의 사례 이상을 테스트하는 것이 나쁜 습관이라고 생각합니다.
정적 가져 오기
JUnit은 각 원시 타입에 대해 적어도 하나의 assertEquals
메소드를 정의하고 Objects에 대해 하나의 assertEquals
메소드를 상당히 많이 정의합니다. 이러한 메서드는 기본적으로 직접 호출 할 수 Assert.assertEquals
다음과 같이 호출해야합니다. Assert.assertEquals
. 그러나 이러한 메소드가 자주 사용되기 때문에 사람들은 거의 항상 정적 가져 오기를 사용하므로 메소드가 클래스 자체의 일부인 것처럼 직접 사용할 수 있습니다.
assertEquals
메소드에 대한 정적 가져 오기를 추가하려면 다음 import 문을 사용하십시오.
import static org.junit.Assert.assertEquals;
다음 정적 가져 오기를 사용하여 assertArrayEquals
, assertNotNull
및 assertFalse
등을 포함한 모든 assert 메서드를 정적으로 가져올 수도 있습니다.
import static org.junit.Assert.*;
정적 가져 오기가없는 경우 :
@Test
public void addition_isCorrect(){
Assert.assertEquals(4 , 2 + 2);
}
정적 가져 오기 사용 :
@Test
public void addition_isCorrect(){
assertEquals(4 , 2 + 2);
}