일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 2022 플러터 설치
- jvm 작동 원리
- 안드로이드 레트로핏 사용법
- 멤버변수
- 큐 자바 코드
- ANR이란
- ar vr 차이
- 안드로이드 유닛테스트란
- 서비스 vs 쓰레드
- 안드로이드 라이선스
- jvm이란
- 서비스 쓰레드 차이
- 2022 플러터 안드로이드 스튜디오
- 안드로이드 유닛 테스트 예시
- 플러터 설치 2022
- android retrofit login
- Rxjava Observable
- 자바 다형성
- 안드로이드 레트로핏 crud
- 안드로이드 os 구조
- 안드로이드 라이선스 종류
- 스택 큐 차이
- 안드로이드 유닛 테스트
- 객체
- 스택 자바 코드
- rxjava cold observable
- android ar 개발
- rxjava disposable
- 클래스
- rxjava hot observable
- Today
- Total
나만을 위한 블로그
[Android] 단위 테스트 시 static 메서드를 테스트하는 방법 본문
바로 이전 글인 단위 테스트에서 쉐어드 프리퍼런스를 사용하는 방법을 공부하다가 찾은 내용인데 글 제목과 다른 내용이라 별도의 포스팅으로 작성한다. 혹시 모르니 이전 글 링크를 아래에 첨부해둔다.
https://onlyfor-me-blog.tistory.com/444
메서드를 만들다 보면 static 메서드도 만들어 사용하는 경우가 있기 마련인데, 되겠지 하면서 static 메서드를 만들어 사용하면 에러가 발생한다. 어떤 에러가 발생하는지 직접 확인해보자.
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public static String iAmCallPrintln() {
return "나불렀어?";
}
그리고 static 메서드를 테스트하는 테스트 메서드를 작성한다.
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import org.junit.Test;
public class SharedTest {
@Test
public void testCallStaticMethod() {
when(MainActivity.iAmCallPrintln()).thenReturn("나불렀어?");
assertEquals("나불렀어?", MainActivity.iAmCallPrintln());
System.out.println("문자열이 일치함");
}
}
이제 테스트 파일을 실행하면 아래와 같이 에러가 발생한다.
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods. Those methods *cannot* be stubbed/verified. Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods. Those methods *cannot* be stubbed/verified. Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
다 보기엔 어질어질하고 귀찮으니 첫 문장만 구글 번역기에 넣고 돌리면 아래와 같이 번역된다.
when()에는 'mock에 대한 메서드 호출' 이어야 하는 인수가 필요하다
이게 무슨 일일까? static 메서드에 대한 차별이다
Mockito 라이브러리 자체가 static 메서드에 대한 테스트를 지원하지 않는 건가? 구글링해보니 몇 블로그의 포스팅이 눈에 띈다.
https://codechacha.com/ko/android-dexmaker-mockito/
Mockito는 final과 static 메서드를 mocking, spying하는 걸 지원하지 않는다
https://medium.com/androiddevelopers/mock-final-and-static-methods-on-android-devices-b383da1363ad
...(중략)...Mockito API는 아직 stub static 메서드에 대한 인터페이스를 제공하지 않으므로 새 인터페이스를 추가해야 한다. 일반 인스턴스 스터빙과의 가장 큰 차이점은 static 메서드의 스터빙을 재설정할 자연스런 위치가 없다는 것이다...(중략)
https://www.baeldung.com/mockito-mock-static-methods
테스트를 작성할 때 static 메서드를 mock해야 하는 상황을 종종 접하게 된다. Mockito 3.4.0 이전에는 static 메서드를 직접 mock할 수 없었다. 오직 PowerMockito가 있어야만 가능했다.
두 번째, 세 번째 링크에 들어가면 아래 코드와 다른 방식으로 static 메서드를 테스트 메서드에서 사용하니 관심있다면 참고하자.
결론은 현재 내가 갖고 있는 Mockito 의존성 문구만으로는 static 메서드를 테스트할 수 없다는 뜻이다.
그러면 어떻게 해야 할까? 다행히 다른 Mockito 의존성 문구가 존재하는데 이걸 쓰면 static 메서드를 테스트할 수 있다.
먼저 앱 수준 gradle에 의존성 문구를 복붙한다.
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test:rules:1.4.0'
testImplementation "org.mockito:mockito-core:3.6.28"
testImplementation "org.mockito:mockito-inline:3.4.0" // <- 이걸 추가
이전 글을 보고 온 사람이라면 맨 마지막 문장만 추가하면 된다. 그리고 메인 액티비티에 대충 static 메서드를 만든다.
import androidx.appcompat.app.AppCompatActivity;
import android.content.SharedPreferences;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sharedPreferences = getSharedPreferences("ddd", 0);
editor = sharedPreferences.edit();
editor.putString("key", "value");
editor.putInt("number", 1);
editor.apply();
}
public static String iAmCallPrintln() {
return "나불렀어?";
}
이제 테스트 코드를 작성한다.
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.MockedStatic;
public class SharedTest {
private static MockedStatic<MainActivity> aMainActivity;
@BeforeClass
public static void setBeforeClass() {
aMainActivity = mockStatic(MainActivity.class);
}
@Test
public void testCallStaticMethod() {
when(MainActivity.iAmCallPrintln()).thenReturn("나불렀어?");
assertEquals("나불렀어?", MainActivity.iAmCallPrintln());
System.out.println("문자열이 일치함");
}
@AfterClass
public static void setAfterClass() {
aMainActivity.close();
}
}
MockedStatic, mockStatic()이란 뉴페이스가 있다. 각각이 무엇인지 간단하게 확인해보자.
https://javadoc.io/static/org.mockito/mockito-core/4.3.1/org/mockito/MockedStatic.html
형식(type)의 static 메서드에 대한 active mock을 나타낸다. mocking은 이 static mock이 생성된 쓰레드에만 영향을 미치며 다른 쓰레드에서 이 객체를 사용하는 건 안전하지 않다. 이 객체의 ScopedMock.close()가 호출되면 static mock이 해제된다. 이 객체가 닫히지 않으면 static mock 객체는 시작 쓰레드에서 활성 상태로 유지된다. 따라서 예를 들어 JUnit 규칙이나 확장을 사용해 명시적으로 관리되는 경우가 아니면 try-with-resources 문 안에서 이 객체를 만드는 것이 좋다. @Mock 어노테이션이 이 유형(type)의 필드 또는 메서드 매개변수에 쓰이는 경우 일반 mock 대신 static mock이 생성된다. static mock은 관련 테스트를 완료하면 활성화되고 해제된다.
대개 try-with-resource 문에서 사용되고 close()를 써서 생성한 static mock을 해제하는 처리도 필요한 듯하다.
그래서 위에서 @AfterClass 어노테이션이 붙은 메서드 안에서 close()를 호출해 static mock을 해제하는 걸 볼 수 있다.
mockStatic()에 대한 설명은 아래와 같다.
https://www.baeldung.com/mockito-mock-static-methods
Mockito 3.4.0부터 Mockito.mockStatic(Class<T> classToMock)를 써서 static 메서드 호출에 대한 호출을 mock할 수 있다. 이 메서드는 범위가 지정된 mock 객체에 대한 MockedStatic 객체를 리턴한다. 범위가 지정된 mock은 mock을 활성화하는 엔티티가 닫아야 한다는 점이 중요하다...(중략)
mockStatic()의 매개변수로 static 메서드가 들어있는 클래스를 넣어 테스트 파일에서 static 메서드를 호출할 수 있게 해주는 것 같다.
위의 코드로 보면 MockedStatic 클래스의 제네릭 안에 메인 액티비티를 넣어서 만든 aMainActivity 객체를 메인 액티비티와 매핑시켜서 mock 객체를 만들어주는 메서드라고 이해했다.
당연한 거지만 @BeforeClass, @AfterClass가 붙은 메서드들은 모두 static인 것에 주의하자.
그런데 메인 액티비티 안의 static 메서드를 호출할 때는 aMainActivity가 아니라 MainActivity를 사용한다. 왜 그럴까? 직접 aMainActivity를 넣어보면 알 수 있다.
저 테스트 파일을 실행하면 녹색 체크 표시가 나오며 테스트 완료가 표시되고, 결과창을 조금 내려보면 println()이 호출된 걸 볼 수 있다.
만약 일부러 틀린 값을 넣고 테스트를 실패시켜보면 이런 화면이 나온다.
참고한 사이트)
'Android' 카테고리의 다른 글
[Android] Gson이란? Gson 사용법 (0) | 2022.02.20 |
---|---|
[Android] espresso를 사용한 UI 테스트(+리사이클러뷰) (0) | 2022.02.07 |
[Android] 단위 테스트 시 쉐어드 프리퍼런스를 사용하는 방법 (0) | 2022.02.05 |
[Android] 웹뷰에 표시된 Node.js GET 요청을 레트로핏으로 받아오기 (0) | 2022.02.03 |
[Android] Retrofit 사용 시 4xx 응답을 파싱하는 방법 (0) | 2022.01.30 |