Robolectric是一個高效但不完美的Android單元測試框架。其通過一系列對底層Android元素的替換來實現對原有元素調用的模擬,從而實現脫離模擬器的測 試。非常值得一提的是,在測試服務器請求時,Robolectric的數據模擬和延時發送模擬,給多線程狀態下的測試提供了很好的解決方法
目前最新版本爲3.0,可以支持API21,你可以在github上面找到他的實例和開源代碼。
Roblectric的github地址
此處所實例的配置是之前2.4版本Roblectric的配置,目前使用最新的AS和3.0版的Robolectric更簡單。
我的配置環境步驟
安裝Android Studio Unit Test插件。
這個插件可以自動幫你關聯測試所在目錄以及維護插件測關係依賴,免去你很多需要配置的地方。
可以通過插件中心(settings-plubgins)在線安裝(Browse repositories…),也可以先下載插件的zip包,離線安裝(Install plugin from disk..)。(插件zip包下載位置:https://github.com/evant/android-studio-unit-test-plugin/blob/master/AndroidStudioUnitTestPlugin.zip?raw=true)添加測試module
下圖是我的項目目錄,測試類單獨在一個module裏面。這樣我們項目裏面是不需要任何改動的,配置都是在測試module裏面配置。
下面是一些比較關鍵的文件:
../robolectric-test/build.gradle
evaluationDependsOn(":app")
apply plugin: 'java'
dependencies {
def androidModule = project(':app')
testCompile project(path: ':app', configuration: 'debugCompile')
def debugVariant = androidModule.android.applicationVariants.find({it.name == 'debug'})
testCompile debugVariant.javaCompile.classpath
testCompile debugVariant.javaCompile.outputs.files
testCompile files(androidModule.plugins.findPlugin("com.android.application").getBootClasspath())
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:robolectric:2.4'
}
tasks.withType(Test) {
scanForTestClasses = false
include "**/*Should.class"
include "**/*Test.class"
include "**/*Tests.class"
}
我這裏使用了AndroidAnnotations所以需要自定義TestRunner類,讓測試方法能夠找到資源文件和清單文件位置。../robolectric-test/src/test/packagename/MyRobolectricTestRunner.java
package example.mislead.com.apptest;
import org.junit.runners.model.InitializationError;
import org.robolectric.AndroidManifest;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.res.Fs;
public class MyRobolectricTestRunner extends RobolectricTestRunner {
// 2.4版本只是支持到API18,最新的3.0可以支持API21了
private static final int MAX_SDK_SUPPORTED_BY_ROBOLECTRIC = 18;
protected static final String projectName = "appTest";
public MyRobolectricTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override
protected AndroidManifest getAppManifest(Config config) {
//set the project path as the root path
int nameLength = projectName.length();
String rootPath = System.getProperty("user.dir", "./");
int index = rootPath.indexOf(projectName); //get the index of projectRootPath of user.dir
if (index == -1) {
throw new RuntimeException("project name not found in user.dir");
}
rootPath = rootPath.substring(0, index + nameLength);
String manifestProperty = rootPath + "/app/src/main/AndroidManifest.xml";
String resProperty = rootPath + "/app/src/main/res";
String assetsProperty = rootPath + "/app/src/main/assets";
return new AndroidManifest(Fs.fileFromPath(manifestProperty), Fs.fileFromPath(resProperty),
Fs.fileFromPath(assetsProperty)) {
@Override
public int getTargetSdkVersion() {
return MAX_SDK_SUPPORTED_BY_ROBOLECTRIC;
}
};
}
}