以前はタスクを完了するために単一のテストが行われていましたが、現在は実際にコードの正しさをテストすることになっており、落とし穴に遭遇しやすくなっています。優れた単一のテスト方法と、私が踏んだ落とし穴について説明します。
1. ログ出力
ここが一番重要だと思います コードをテストするときは、出力されたログを見てコードがどこに行くのかを判断する必要があります 以前は行範囲を気にしすぎていましたが、この部分はあまり気にしていませんでした. この部分の対応にかなり時間がかかりました。
Log.xxx はコンソールにログを出力できず、System.out.print のみが表示されるため、その出力を System.out.print に変換するモック Log の静的メソッドが必要です。
PowerMockito.mockStatic(Log.class);
Mockito.when(Log.e(anyString(), anyString())).then(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
String TAG = (String) invocation.getArguments()[0];
String msg = (String) invocation.getArguments()[1];
System.err.println(String.format("Error: /%s: %s", TAG, msg));
return null;
}
});
他のログ レベルも同様で、いくつかのメイン メソッドはクラス内に記述でき、後で呼び出すのに便利です。
ここで、PowerMockito は戻り値を空にすることはできないことに注意してください (私が見つけられなかったのでしょうか?)。
2. テストテンプレート
一般的にはRobolectricとPowerMockを併用しますが、マッチングする際にいくつかのパラメータを設定しないと両者が競合する可能性が高く、例えば両方を併用するとRobolectric配下のRuntimeEnvironment.applicationがnullになってしまうためテンプレートを用意しています. 統合テスト環境と同等:
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
// 这个就是为了避免PowerMock把Robolectric的一些东西mock掉
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@PrepareForTest({Log.class})
public class TestTemplate {
// 这个相当于setUp()函数,不加是mock不了的.
@Rule
public PowerMockRule rule = new PowerMockRule();
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
MockLogUtils.mockLog();
}
}
頼る:
testImplementation "org.powermock:powermock-module-junit4:1.6.6"
testImplementation "org.powermock:powermock-module-junit4-rule:1.6.6"
testImplementation "org.powermock:powermock-api-mockito:1.6.6"
testImplementation "org.powermock:powermock-classloading-xstream:1.6.6"
testImplementation 'org.robolectric:robolectric:3.8'
// 以下非必须,robolectric还有很多这样的影子库
testImplementation 'org.robolectric:shadows-httpclient:3.3.2'
PS: このテンプレートには欠陥があります。Context をモックしたい場合、robo と競合します。この場合、別のテスト クラスを作成し、PowerMock.runner を使用してテストします。
3. テストスキル:
(1) 分岐をカバーするだけでなく、
空のパラメータを渡す、関数を複数回呼び出す、実行時間に注意するなど、境界についても考慮する必要があります(合計してみましょう)
(2) ホワイトボックスは良いものです: ホワイトボックス
は多くのリフレクション関数 setInternalState と getInternalState を完成させます, これらはテスト対象のオブジェクトのプロパティ変更を設定および取得するのに直接役立ちます. いくつかの関数が実行された後, それらは verify または で検証できます主張します。
(3) 例外スローのテスト:
Test(expect = Exception.class) で、expect は必要な例外を追加します。
(4) when...thenReturn と doReturn...when の違い:
(a)when(...) thenReturn(...) は、実際のメソッドを呼び出したくない場合、実際のメソッドを呼び出します。ただしモックしたい場合は、このメソッドを使用しないでください。
(b)doReturn(…) when(…) は実際のメソッドを呼び出しません。
(5) PrepareForTestの問題。
最近問題が発生したので、クラス レベルでアノテーション PrepareForTest({Log.class}) を追加し、メソッドのテスト時にメソッド レベルでも PrepareForTest({XXX.class}) を追加した結果、このメソッドを実行すると、「Log」と表示されました。.class メソッドには prepare がありません。理由はわかりません。prepare が分離されると機能しなくなることがわかりました。2 つのクラスがクラスまたはメソッドで準備されている場合、同時に、大丈夫です。
(6) 非同期メソッド
のテスト 新しいスレッド内で実行したり、スレッドプールを使用して実行したりするなど、テスト対象のメソッドが非同期で実行される場合、Test メソッドは System.exit ( ) ドロップすると、この時点では子スレッドが作成されていない可能性があります。Test メソッドの最後に Thread.sleep() を配置して、子スレッドが実行されるのを待つことができます。
(7) テストハンドラー
Handler は Android 独自のものなので、ローカルの単一テストでは直接スキップされますが、Handler のコールバックが正しい場合は、それをモックして、パラメーターを取得した後、手動で実行することができます。
@Mock
Handler handler;
@Before
@PrepareForTest({ Handler.class})
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mock(Handler.class);
// mock它的post方法,其它类似
PowerMockito.doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Runnable runnable = (Runnable) invocation.getArguments()[0];
runnable.run();
return null;
}
}).when(handler).post((Runnable) any());
}
(8) オブジェクトをスパイすると、返されたオブジェクトをモックすることができます。スパイとモックの違いは次のとおりです:
①モックされたオブジェクトは完全に偽物であり、それを呼び出すメソッドは呼び出されません. 通常、テスト対象オブジェクトの依存オブジェクトに使用されます。
②spy、実際のメソッドを呼び出す必要がある場合は、実際にこのクラスオブジェクトを作成する必要がありますが、現時点でそのメソッドの一部をモックしたい場合、objはモックできないため、when(obj)を呼び出すときにエラーが報告されます。この時点で、モックしたい場合は、単に spy(obj) を実行すると、新しい obj が返され、それをモックできます。モックされたオブジェクトと比較すると、それは「部分的なシミュレーション」です。その一部をモックしていません。メソッドを使用しても、実際のメソッドが呼び出されます。