介紹
setup:即new 出待測試的類,設置一些前提條件
執行動作:即調用被測類的被測方法,並獲取返回結果
驗證結果:驗證獲取的結果跟預期的結果是一樣的
Junit4
java測試框架
testCompile ‘junit:junit:4.12’
@Before
@Test
(expected = IllegalArgumentException.class) 驗證是否拋該異常
@After
@Ignore 跑所有測試方法忽略該方法
除了在AndroidStudio裏面運行,你還可以在命令行通過gradle testDebugUnitTest,或者是gradle testReleaseUnitTest,分別運行debug和release版本的unit testing
Mockito的
java界使用最廣法的mock框架
概念就是創建一個類的虛假對象,在測試環境中,用來替換掉真實的添加依賴
repositories { jcenter() }
dependencies {
testCompile "org.mockito:mockito-core:
}
api
驗證方法調用:
Mockito.verify(objectToVerify,Mockito.times(3)).methodToVerify(Mockito.anyString());
設定返回值,方便測試:
Mockito.when(mockObject.targetMethod(args)).thenReturn(desiredReturnValue);
- 執行特定動作,如訪問網絡數據
Mockito.doAnswer(desiredAnswer).when(mockObject).targetMethod(args); Mockito.doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { //這裏可以獲得傳給performLogin的參數 Object[] arguments = invocation. //callback是第三個參數 NetworkCallback callback = (NetworkCallback) arguments[ callback.onFailure(500, "Server error"); return 500; } }).when(mockUserManager).performLogin(anyString(), anyString(), any(NetworkCallback.class));
ps: Mockito.spy與mock的唯一區別就是默認行爲不一樣:spy對象的方法默認調用真實的邏輯,mock對象的方法默認什麼都不做,或直接返回默認值。
dagger2
能不用就不用!
- 依賴
buildscript {
...
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
//添加apt插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
...
```
//應用apt插件
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
...
//引入dagger2
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
//java註解
provided 'org.glassfish:javax.annotation:10.0-b28'
}
``````
依賴注入概念
我們平常使用的構造函數,需要傳入對象去使用,就算是一種依賴注入,然而當我們需要的對象嵌套越來越多的時候,就會顯得冗餘,這個時候dagger2就出現了。client –> Component(工廠管理員)–>Module(生產依賴的工廠)–>Dependency
api
Module @Module 修飾類 @Provides 修飾方法 @Singleton 修飾方法,獲取單例節省資源 Component @Component (modules={OneModule.class}) 修飾類,指定管理的生產Dependency的工廠 1. 定義返回Dependency的抽象方法即可 2. 先在Component 定義inject(Object obj方法 定義成員變量@Inject修飾即可 ps: 不能繼承,都要實現inject(this)方法才能生成實例
單元測試
//需要測試
public class LoginActivity extends AppCompatActivity {
@Inject
LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ComponentHolder.getAppComponent().inject(this);
findViewById(R.id.login).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String username = ((EditText) findViewById(R.id.username)).getText().toString();
String password = ((EditText) findViewById(R.id.password)).getText().toString();
mLoginPresenter.login(username, password);
}
});
}
}
//測試
public class DaggerUtils {
public static final AppModule appModule = spy(new AppModule(RuntimeEnvironment.application));
public static void setupDagger() {
AppComponent appComponent = DaggerAppComponent.builder().appModule(appModule).build();
ComponentHolder.setAppComponent(appComponent);
}
}
//-------
@RunWith(RobolectricGradleTestRunner.class) //Robolectric相關,看不懂的話忽略
@Config(constants = BuildConfig.class, sdk = 21) //同上
public class LoginActivityTest {
@Test
public void testActivityStart() {
@Test
public void testActivityStart() {
DaggerUtils.setupDagger();
LoginPresenter mockLoginPresenter = mock(LoginPresenter.class);
Mockito.when(DaggerUtils.appModule.provideLoginPresenter(any(UserManager.class), any(PasswordValidator.class))).thenReturn(mockLoginPresenter);
LoginActivity loginActivity = Robolectric.setupActivity(LoginActivity.class);
((EditText) loginActivity.findViewById(R.id.username)).setText("xiaochuang");
((EditText) loginActivity.findViewById(R.id.password)).setText("xiaochuang is handsome");
loginActivity.findViewById(R.id.login).performClick();
verify(mockLoginPresenter).login("xiaochuang", "xiaochuang is handsome"); //pass!
}
}
}
Robolectric使用
Robolectric就是一個能夠讓我們在JVM上跑測試時夠調用安卓的類的框架
- android.jar不完整,android相關方法只是拋出異常RuntimeException(“stub!!”)
- 依賴
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.2.1"
- api
```
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest {
@Test
public void testMainActivity() {
MainActivity mainActivity = Robolectric.setupActivity(MainActivity.class);
mainActivity.findViewById(R.id.textView1).performClick();
Intent expectedIntent = new Intent(mainActivity, SecondActivity.class);
ShadowActivity shadowActivity = Shadows.shadowOf(mainActivity);
Intent actualIntent = shadowActivity.getNextStartedActivity();
Assert.assertEquals(expectedIntent, actualIntent);
}
}
```