Unit Test
Start
简单了解一下,如何依赖 Junit 进行 Java 代码的单元测试。依旧如何借助 Robolectric 进行 Android 方面的单元测试,主要是 Context 的获取。最后就网络请求的单元测试,简单叙述一下。
JUnit
1
| testImplementation 'junit:junit:4.12'
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Tools { private static final String TAG = "Tools";
public static String getCurrentTime() { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss", Locale.CHINA); Date curDate = new Date(System.currentTimeMillis()); return simpleDateFormat.format(curDate); }
public static String getCurrentTime(long tempStap) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年", Locale.CHINA); Date curDate = new Date(tempStap); return simpleDateFormat.format(curDate); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class ToolsUnitTest {
@BeforeClass public static void setUp() { System.out.println( ToolsUnitTest.class.getSimpleName()+ "=====单元测试开始"); }
@AfterClass public static void end() { System.out.println( ToolsUnitTest.class.getSimpleName()+ "=====单元测结束"); }
@Test(expected = NullPointerException.class) public void getCurrentTimeTest() { assertNotEquals("1111",Tools.getCurrentTime()); assertEquals("2019年",Tools.getCurrentTime(System.currentTimeMillis())); } }
|
执行顺序: @BeforeClass –> @Before –> @Test –> @After –> @AfterClass
都是 Assert 的静态方法,对有返回值的方法,用断言非常好用,你甚至可以测试异常
- 高级用法
- @RunWith(Parameterized.class) 参数化
- assertThat用法
- @Rule用法
1
| public static String getAppVersion(Context mContext) { ... }
|
Android 中的 context 怎么搞 ?
Robolectric
一定要添加以下配置,否则将导致单元测试运行异常
一定要添加以下配置,否则将导致单元测试运行异常
一定要添加以下配置,否则将导致单元测试运行异常
1 2 3 4 5
| testOptions { unitTests { includeAndroidResources = true } }
|
- 在 gradle.properties 中添加
1
| android.enableUnitTestBinaryResources=true
|
example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @RunWith(RobolectricTestRunner::class) @Config(sdk = [27]) class RobolectricUnitTest {
@Test fun assertContext() { val context = ApplicationProvider.getApplicationContext<Context>()
val version = AppUtils.getAppVersion(context) println("version ==$version")
assertEquals("1.0", version) }
companion object {
@BeforeClass fun setup() { SoLoader.setInTestMode() } } }
|
单元测试也可以用 Kotlin 写,😬😬😬
Context 是什么 ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @RunWith(RobolectricTestRunner.class) @Config(sdk = 28, manifest = Config.NONE) public class ContextTest {
@Test public void useContextTest() throws Utils.SystemUtilsException { Context context = ApplicationProvider.getApplicationContext();
System.out.println("context ===" + context.getClass().getName());
assertEquals(Constants.PACKAGE_NAME, Utils.getPackageName(context)); assertEquals(Constants.PACKAGE_VERSION, Utils.getPackageVersionName(context)); }
@Test public void screenInfoTest() { Context context = ApplicationProvider.getApplicationContext(); Resources resources = context.getResources(); DisplayMetrics displayMetrics = resources.getDisplayMetrics(); float density = displayMetrics.density; float width = displayMetrics.widthPixels; float height = displayMetrics.heightPixels;
System.out.println("density==" + density); System.out.println("width ==" + width); System.out.println("height ==" + height); } }
|
-output
1 2 3
| density==1.0 width ==320.0 height ==470.0
|
Robolectric 很强大,可以在不使用真机及模拟器的情况下,进行 UI 测试
Robolectric 可以认为是一个虚拟的模拟器
网络测试
网络测试有以下困难
- 请求结果是异步返回的,无法直接进行断言,需要进行同步转换
- 后端会做 cookie 和 headers 的校验,Robolectric 只是模拟器,请求时没有这些信息。
对 okhttp 添加 intercept ,拦截返回值,进行任意返回结果,测试后续逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test public void netTest() {
final Retrofit mRetrofit = initRetrofit();
GankApi mGankApi = mRetrofit.create(GankApi.class); Observable<GankAndroid> mAndroidObservable = mGankApi.getData("10/1"); mAndroidObservable.subscribe(gankAndroid -> { doAssert(gankAndroid.getResults().get(0)); }, throwable -> System.out.println("fail")); }
private void doAssert(GankAndroid.ResultsEntity entity) { assertEquals("5d423ff19d2122031ea52264", entity.get_id()); assertEquals("web", entity.getSource()); assertEquals("Android", entity.getType()); assertEquals("潇湘剑雨", entity.getWho()); }
|
这个就可以对网络请求回来的数据,进行各种业务逻辑的测试了。
但是也可以添加 interceptor ,创建特定的返回结果
- 添加 Interceptor 拦截返回结果,方便测试后续逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test public void netWithInterceptTest() { String json = "404"; SimpleIntercept errorIntercept = new SimpleIntercept(json, 404);
final Retrofit mRetrofit = initRetrofit(errorIntercept);
GankApi mGankApi = mRetrofit.create(GankApi.class); Observable<GankAndroid> mAndroidObservable = mGankApi.getData("10/1"); mAndroidObservable.subscribe(gankAndroid -> { }, throwable -> { assertEquals("HTTP 404 404", throwable.getMessage()); }); }
|
SimpleIntercept
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class SimpleIntercept implements Interceptor {
private static final String APPLICATION_JSON = "application/json";
private String expectedResult; private int code;
public SimpleIntercept(@NonNull String expectedResult, int code) { this.expectedResult = expectedResult; this.code = code; }
@Override public Response intercept(Chain chain) { Response response = new Response.Builder() .code(code) .message(expectedResult) .request(chain.request()) .protocol(Protocol.HTTP_1_1) .body(ResponseBody.create(MediaType.parse(APPLICATION_JSON), expectedResult)) .addHeader("content-type", APPLICATION_JSON) .build(); return response; } }
|
参考资料
JUnit 入门
Android单元测试(四):Robolectric框架的使用