介紹
setup:即new 出待測試的類,設(shè)置一些前提條件
執(zhí)行動(dòng)作:即調(diào)用被測類的被測方法,并獲取返回結(jié)果
驗(yàn)證結(jié)果:驗(yàn)證獲取的結(jié)果跟預(yù)期的結(jié)果是一樣的
Junit4
java測試框架
testCompile 'junit:junit:4.12'
@Before
@Test
(expected = IllegalArgumentException.class) 驗(yàn)證是否拋該異常
@After
@Ignore 跑所有測試方法忽略該方法
除了在AndroidStudio里面運(yùn)行,你還可以在命令行通過gradle testDebugUnitTest,或者是gradle testReleaseUnitTest,分別運(yùn)行debug和release版本的unit testing
Mockito的使用
參考 https://segmentfault.com/u/chriszou
java界使用最廣法的mock框架
概念就是創(chuàng)建一個(gè)類的虛假對象,在測試環(huán)境中,用來替換掉真實(shí)的添加依賴
repositories { jcenter() }
dependencies {
testCompile "org.mockito:mockito-core:
}
- api
- 驗(yàn)證方法調(diào)用:
Mockito.verify(objectToVerify,Mockito.times(3)).methodToVerify(Mockito.anyString());
- 設(shè)定返回值,方便測試:
Mockito.when(mockObject.targetMethod(args)).thenReturn(desiredReturnValue);
- 執(zhí)行特定動(dòng)作,如訪問網(wǎng)絡(luò)數(shù)據(jù)
Mockito.doAnswer(desiredAnswer).when(mockObject).targetMethod(args);
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
//這里可以獲得傳給performLogin的參數(shù)
Object[] arguments = invocation.getArguments();
//callback是第三個(gè)參數(shù)
NetworkCallback callback = (NetworkCallback) arguments[2];
callback.onFailure(500, "Server error");
return 500;
}
}).when(mockUserManager).performLogin(anyString(), anyString(), any(NetworkCallback.class));
ps: Mockito.spy與mock的唯一區(qū)別就是默認(rèn)行為不一樣:spy對象的方法默認(rèn)調(diào)用真實(shí)的邏輯,mock對象的方法默認(rèn)什么都不做,或直接返回默認(rèn)值。
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'
}
}
...
//應(yīng)用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'
}
- 依賴注入概念
我們平常使用的構(gòu)造函數(shù),需要傳入對象去使用,就算是一種依賴注入,然而當(dāng)我們需要的對象嵌套越來越多的時(shí)候,就會顯得冗余,這個(gè)時(shí)候dagger2就出現(xiàn)了。
client --> Component(工廠管理員)-->Module(生產(chǎn)依賴的工廠)-->Dependency
api
Module
@Module 修飾類
@Provides 修飾方法
@Singleton 修飾方法,獲取單例節(jié)省資源
Component
@Component
(modules={OneModule.class}) 修飾類,指定管理的生產(chǎn)Dependency的工廠
1. 定義返回Dependency的抽象方法即可
2. 先在Component 定義inject(Object obj方法
定義成員變量@Inject修飾即可
ps: 不能繼承,都要實(shí)現(xiàn)inject(this)方法才能生成實(shí)例單元測試
//需要測試
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相關(guān),看不懂的話忽略
@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就是一個(gè)能夠讓我們在JVM上跑測試時(shí)夠調(diào)用安卓的類的框架
- android.jar不完整,android相關(guān)方法只是拋出異常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);
}
}
- 參考: http://chriszou.com/2016/06/07/android-unit-testing-everything-you-need-to-know.html