Android
JUnitによるAndroidのユニットテスト
サーチ…
備考
- Vogella: JUnitによるユニットテスト
- Junit注釈: java2novice.com
- アサートクラス: junit.org
- JUnit Api: tutorialspoint.com
- Anroid Testing Medium.comの投稿
ローカルユニットテストの作成
テストクラスは/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メソッドをテストケースとして実行できることをJUnitに伝えます。
@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
public voidメソッドに注釈を@Before
と、そのメソッドはTestメソッドの前に実行されます。
@After: Beforeメソッドで外部リソースを割り当てる場合は、テストの実行後に外部リソースを解放する必要があります。 @After
public voidメソッドに注釈を@After
と、そのメソッドはTestメソッドの後に実行されます。 @After
メソッドまたはTestメソッドが例外をスローした場合でも、すべての@After
メソッドが実行されることが保証されています。
ヒント簡単にAndroid Studioでテストクラスを作成する
- テストクラスを作成するクラス名の上にカーソルを置きます。
- Alt + Enter(Windows)を押します。
- Create Testを選択し、Returnを押します。
- テストメソッドを作成するメソッドを選択し、[OK]をクリックします。
- テストクラスを作成するディレクトリを選択します。
- あなたは完了です、これはあなたの最初のテストです。
ヒント簡単にAndroid Studioでテストを実行する
- パッケージを右クリックしてテストします。
- 実行テストを選択してください...
- パッケージ内のすべてのテストが一度に実行されます。
Androidコンポネックスからビジネスロジックへの移行
ローカルJVM単体テストの多くの価値は、アプリケーションの設計方法にあります。 Androidコンポーネントからビジネスロジックを切り離すことができるような方法で設計する必要があります。ここでは、 Model-View-Presenterパターンを使用した方法の例を示します 。これを実践するには、ユーザー名とパスワードのみを使用する基本的なサインアップ画面を実装します。私たちのAndroidアプリは、ユーザが提供するユーザ名が空ではなく、パスワードが少なくとも8桁で少なくとも1桁の数字であることを確認する責任があります。ユーザー名/パスワードが有効な場合は、サインアップ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単体テストを作成できるようになりました。
私たちが1つの責任を持つクラスごとに遵守すること、クラスを追加することなど、アーキテクチャーの変更には他にもさまざまな意味があることに注意してください。これは私がこれを実行する方法の副作用ですMVPスタイルによるデカップリング。 MVPはこの問題を解決する方法の1つに過ぎませんが、 MVVMのような他の選択肢があります。あなたはあなたのために働く最高のシステムを選ぶだけです。
JUnitを使い始める
セットアップ
JUnitを使用してAndroidプロジェクトのユニットテストを開始するには、プロジェクトにJUnit依存関係を追加する必要があります。ユニットテストのソースコードを含むテストソースセットを作成する必要があります。 Android Studioで作成されたプロジェクトには、すでにJUnitの依存関係とテストソースセットが含まれていることがよくあります
あなたのモジュールに次の行を追加しますbuild.gradle
依存関係内のファイルClosure
:
testCompile 'junit:junit:4.12'
JUnitテストクラスは、 test
という名前の特別なソースセットにあり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スタジオで作成されたプロジェクトで見つかることが多く、参照用にここに含まれています。
テストの作成
test
ソースセット内に新しいクラスを作成します。
プロジェクトビューでテストソースセットを右クリックし、New
>Java class
選択します。
最もよく使用される命名パターンは、Test
追加するテストのクラス名を使用することです。だから、StringUtilities
なりStringUtilitiesTest
。@RunWith
アノテーションを追加する
@RunWith
アノテーションは、テストクラスで定義するテストをJUnitに実行させるために必要です。デフォルトのJUnitランナー(JUnit 4用)はBlockJUnit4ClassRunner
が、これを直接実行する代わりに、デフォルトのJUnitランナーの短縮形である別名JUnit4
を使用する方が便利です。@RunWith(JUnit4.class) public class StringUtilitiesTest { }
テストを作成する
単体テストは、基本的に単なるテストであり、ほとんどの場合、実行すると失敗しないはずです。言い換えれば、例外をスローするべきではありません。テストメソッドの中では、ほとんどの場合、特定の条件が満たされているかどうかを確認するアサーションがあります。アサーションが失敗した場合、それはメソッド/テストを失敗させる例外をスローします。テストメソッドは常に@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 ...
]をクリックしRun ...
。
例外
JUnitは、メソッドが特定の入力に対して特定の例外をスローするかどうかをテストするためにも使用できます。
この例では、ブール形式(入力)が認識されないか不明な場合、次のメソッドが実際に例外をスローするかどうかをテストします。
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);
}
}
@Test
アノテーションにexpected
パラメータを追加することによって、スローされると予想される例外を定義することができます。この例外が発生しない場合、ユニットテストは失敗し、例外が実際にスローされた場合は成功します。
@Test(expected = IllegalArgumentException.class)
public void parseBoolean_parsesInvalidFormat_throwsException(){
StringUtilities.parseBoolean("Hello JUnit");
}
これはうまくいきますが、メソッド内のテストケースが1つに制限されます。 1つの方法で複数のケースをテストしたい場合があります。この制限を克服するためによく使用される手法は、 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){
}
}
注:ユニットテストの中で1つ以上のケースをテストすることは悪い習慣と考える人もいます。
静的インポート
JUnitは、各プリミティブ型に対して少なくとも1つのかなりのassertEquals
メソッドを定義し、Objectsに対して1つのメソッドを定義しています。これらのメソッドは、デフォルトでは直接呼び出すことができず、 Assert.assertEquals
ように呼び出す必要があります。しかし、これらのメソッドは非常に頻繁に使用されるため、メソッドは静的なインポートを使用するため、メソッドがクラスの一部であるかのように直接使用できます。
assertEquals
メソッドの静的インポートを追加するには、次のimport文を使用します。
import static org.junit.Assert.assertEquals;
次の静的インポートを使用して、 assertArrayEquals
、 assertNotNull
、およびassertFalse
などのすべてのアサートメソッドを静的にインポートすることもできます。
import static org.junit.Assert.*;
静的インポートを使用しない場合:
@Test
public void addition_isCorrect(){
Assert.assertEquals(4 , 2 + 2);
}
静的インポートの場合:
@Test
public void addition_isCorrect(){
assertEquals(4 , 2 + 2);
}