HarmonyOS非UI單元測(cè)試在DevEco Studio上的應(yīng)用
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
一、什么是單元測(cè)試
單元測(cè)試是測(cè)試某個(gè)類(lèi)的某個(gè)方法能否正常工作的一種手段。
單元測(cè)試的粒度:一般一個(gè)public方法需要一個(gè)test case
二、單元測(cè)試目的
- 驗(yàn)收(改動(dòng)和重構(gòu))
- 快速驗(yàn)證邏輯
- 優(yōu)化代碼設(shè)計(jì)
三、單元測(cè)試工具
junit4 + mockito + powermock
junit4:JUnit是Java最基礎(chǔ)的測(cè)試框架,主要的作用就是斷言
Mock的作用:解決測(cè)試類(lèi)對(duì)其他類(lèi)的依賴(lài)問(wèn)題。Mock的類(lèi)所有方法都是空,所有變量都是初始值。
PowerMock:PowerMock是Mockito的擴(kuò)展增強(qiáng)版,支持mock private、static、final方法和類(lèi),還增加了很多反射方法可以方便修改靜態(tài)和非靜態(tài)成員等。功能比Mockito增加很多。
- // build.gradle中引入powermock
- testImplementation 'org.powermock:powermock-api-mockito2:2.0.2'
- testImplementation 'org.powermock:powermock-module-junit4:2.0.2'
四、單元測(cè)試流程
1、新建測(cè)試類(lèi)(快捷導(dǎo)航鍵: ctrl+shift+T),新建測(cè)試用例名
2、setUp 初始化一些公共的東西
3、編寫(xiě)測(cè)試代碼,執(zhí)行操作
4、驗(yàn)證結(jié)果
一般我們依據(jù)被測(cè)方法是否有返回值選用不同的驗(yàn)證方法。
有返回值的,直接調(diào)用該方法得到返回結(jié)果,使用JUnit的Asset驗(yàn)證結(jié)果;
沒(méi)有返回值的,則看方法最終調(diào)用了依賴(lài)對(duì)象的哪個(gè)方法,然后再校驗(yàn)依賴(lài)對(duì)象的該方法有沒(méi)有被調(diào)用,以及獲取到的參入?yún)?shù)是否正確
舉例說(shuō)明:
- public void login(String username, String password) {
- if (username == null || username.length() == 0) {
- return;
- }
- if (password == null || password.length() < 6) {
- return;
- }
- mUserManager.performLogin(username, password);
- }
我們要驗(yàn)證該login方法是否正確,則依據(jù)傳入的參數(shù),判斷mUserManager的performLogin方法是否得要了調(diào)用。
五、基礎(chǔ)用法
常見(jiàn)注解:
- @Before: 如果一個(gè)方法被@Before修飾過(guò)了,那么在每個(gè)測(cè)試方法調(diào)用之前,這個(gè)方法都會(huì)得到調(diào)用。
- @After: 每個(gè)測(cè)試方法運(yùn)行結(jié)束之后,會(huì)得到運(yùn)行的方法
- @Test:如果一個(gè)方法被@Before修飾過(guò)了,那么這個(gè)方法為可執(zhí)行的測(cè)試用例,注解設(shè)置expected參數(shù) 可驗(yàn)證一個(gè)方法是否拋出了異常
- @Ignore:忽略的測(cè)試方法
- @RunWith 指定該測(cè)試類(lèi)使用某個(gè)運(yùn)行器
- @Rule:重新制定測(cè)試類(lèi)中方法的行為,可以理解為在測(cè)試用例執(zhí)行前和執(zhí)行后插樁
- @Mock: 創(chuàng)建一個(gè)類(lèi)的虛假的對(duì)象,在測(cè)試環(huán)境中,用來(lái)替換掉真實(shí)的對(duì)象,以達(dá)到兩大目的:
a.驗(yàn)證這個(gè)對(duì)象的某些方法的調(diào)用情況,調(diào)用了多少次,參數(shù)是什么等等
b.指定這個(gè)對(duì)象的某些方法的行為,返回特定的值,或者是執(zhí)行特定的動(dòng)作
注意:mock出來(lái)的對(duì)象并不會(huì)自動(dòng)替換掉正式代碼里面的對(duì)象,你必須要有某種方式把mock對(duì)象應(yīng)用到正式代碼里面
junit框架中Assert類(lèi)的常用方法
- assertEquals: 斷言傳入的預(yù)期值與實(shí)際值是相等的
- assertNotEquals: 斷言傳入的預(yù)期值與實(shí)際值是不相等的
- assertArrayEquals: 斷言傳入的預(yù)期數(shù)組與實(shí)際數(shù)組是相等的
- assertNull: 斷言傳入的對(duì)象是為空
- assertTrue: 斷言條件為真
- assertFalse: 斷言條件為假
- assertSame: 斷言?xún)蓚€(gè)對(duì)象引用同一個(gè)對(duì)象,相當(dāng)于“==”
Mockito的使用
Mockito的使用主要分三步:Mock/spy對(duì)象 + 打樁 + 驗(yàn)證
示例:
- when(mockObj.methodName(params)).thenReturn(result)
- mock: 所有方法都是空方法,非void方法都將返回默認(rèn)值,比如int方法返回0,對(duì)象方法將返回null,而void方法將什么都不做。 適用于類(lèi)對(duì)外部依賴(lài)較多,只關(guān)新少數(shù)函數(shù)的具體實(shí)現(xiàn);
- spy:跟正常類(lèi)對(duì)象一樣,是正常對(duì)象的替身。適用場(chǎng)景跟mock相反,類(lèi)對(duì)外依賴(lài)較少,關(guān)心大部分函數(shù)的具體實(shí)現(xiàn)。
四種Mock方式:
- 普通方法:
- @Test
- public void testIsNotNull(){
- Person mPerson = mock(Person.class); //<--使用mock方法
- assertNotNull(mPerson);
- }
- 注解方法:
- public class MockitoAnnotationsTest {
- @Mock //<--使用@Mock注解
- Person mPerson;
- @Before
- public void setup(){
- MockitoAnnotations.initMocks(this); //<--初始化
- }
- @Test
- public void testIsNotNull(){
- assertNotNull(mPerson);
- }
- }
- 運(yùn)行器方法:
- @RunWith(MockitoJUnitRunner.class) //<--使用MockitoJUnitRunner
- public class MockitoJUnitRunnerTest {
- @Mock //<--使用@Mock注解
- Person mPerson;
- @Test
- public void testIsNotNull(){
- assertNotNull(mPerson);
- }
- }
- MockitoRule方法:
- public class MockitoRuleTest {
- @Mock //<--使用@Mock注解
- Person mPerson;
- @Rule //<--使用@Rule
- public MockitoRule mockitoRule = MockitoJUnit.rule();
- @Test
- public void testIsNotNull(){
- assertNotNull(mPerson);
- }
- }
常用參數(shù)匹配
- anyObject() 匹配任何對(duì)象
- any(Class
type) 與anyObject()一樣 - any() 與anyObject()一樣 (慎用,有些場(chǎng)景會(huì)導(dǎo)致測(cè)試用例執(zhí)行失敗)
- anyBoolean() 匹配任何boolean和非空Boolean
- anyByte() 匹配任何byte和非空Byte
- anyInt() 匹配任何int和非空Integer
- anyString() 匹配任何非空String
- …
常用打樁方法
- thenReturn(T value) 設(shè)置要返回的值
- thenThrow(Throwable… throwables) 設(shè)置要拋出的異常
- thenAnswer(Answer answer) 對(duì)結(jié)果進(jìn)行攔截
- doReturn(Object toBeReturned) 提前設(shè)置要返回的值
- doThrow(Throwable… toBeThrown) 提前設(shè)置要拋出的異常
- doAnswer(Answer answer) 提前對(duì)結(jié)果進(jìn)行攔截
- doCallRealMethod() 調(diào)用某一個(gè)方法的真實(shí)實(shí)現(xiàn)
- doNothing() 設(shè)置void方法什么也不做
PowerMock使用
首先使用PowerMock必須加注解@PrepareForTest和@RunWith(PowerMockRunner.class)。注解@PrepareForTest里寫(xiě)的是靜態(tài)方法所在的類(lèi),如果@RunWith被占用。這時(shí)我們可以使用@Rule來(lái)解決
- @Rule
- public PowerMockRule rule = new PowerMockRule();
- mock靜態(tài)方法
- @RunWith(PowerMockRunner.class)
- public class PowerMockitoStaticMethodTest {
- @Test
- @PrepareForTest({Banana.class})
- public void testStaticMethod() {
- PowerMockito.mockStatic(Banana.class); //<-- mock靜態(tài)類(lèi)
- Mockito.when(Banana.getColor()).thenReturn("綠色");
- Assert.assertEquals("綠色", Banana.getColor());
- //更改類(lèi)的私有屬性
- Whitebox.setInternalState(Banana.class, "COLOR", "紅色的");
- }
- }
- mock私有方法
- @RunWith(PowerMockRunner.class)
- public class PowerMockitoPrivateMethodTest {
- @Test
- @PrepareForTest({Banana.class})
- public void testPrivateMethod() throws Exception {
- Banana mBanana = PowerMockito.mock(Banana.class);
- PowerMockito.when(mBanana.getBananaInfo()).thenCallRealMethod();
- PowerMockito.when(mBanana, "flavor").thenReturn("苦苦的");
- Assert.assertEquals("苦苦的黃色的", mBanana.getBananaInfo());
- //驗(yàn)證flavor是否調(diào)用了一次
- PowerMockito.verifyPrivate(mBanana).invoke("flavor");
- }
- }
- mock final方法,使用方式同 mock 私有方法
- mock 構(gòu)造方法
- @Test
- @PrepareForTest({Banana.class})
- public void testNewClass() throws Exception {
- Banana mBanana = PowerMockito.mock(Banana.class);
- PowerMockito.when(mBanana.getBananaInfo()).thenReturn("大香蕉");
- //如果new新對(duì)象,則返回這個(gè)上面設(shè)置的這個(gè)對(duì)象
- PowerMockito.whenNew(Banana.class).withNoArguments().thenReturn(mBanana);
- //new新的對(duì)象
- Banana newBanana = new Banana();
- Assert.assertEquals("大香蕉", newBanana.getBananaInfo());
- }
@Rule用法
自定義@Rule很簡(jiǎn)單,就是實(shí)現(xiàn)TestRule 接口,實(shí)現(xiàn)apply方法。
- public class MyRule implements TestRule {
- @Override
- public Statement apply(final Statement base, final Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- // evaluate前執(zhí)行方法相當(dāng)于@Before
- String methodName = description.getMethodName(); // 獲取測(cè)試方法的名字
- System.out.println(methodName + "測(cè)試開(kāi)始!");
- base.evaluate(); // 運(yùn)行的測(cè)試方法
- // evaluate后執(zhí)行方法相當(dāng)于@After
- System.out.println(methodName + "測(cè)試結(jié)束!");
- }
- };
- }
- }
六、RxJava與單元測(cè)試
RxJava的火熱程度不用多說(shuō),由于其基于事件流的鏈?zhǔn)秸{(diào)用、邏輯簡(jiǎn)潔 & 使用簡(jiǎn)單的特點(diǎn),深受各大開(kāi)發(fā)者的歡迎。我們經(jīng)常用它來(lái)進(jìn)行線(xiàn)程的切換操作
例如:
- public void threadSwitch() {
- Observable.just("one", "two", "three", "four", "five")
- .subscribeOn(Schedulers.newThread())
- .observeOn(OpenHarmonySchedulers.mainThread())
- .subscribe(new Observer<String>() {
- @Override
- public void onSubscribe(@NonNull Disposable d) {
- }
- @Override
- public void onNext(@NonNull String s) {
- System.out.println(s);
- if (callBack != null) {
- callBack.success(s);
- }
- }
- @Override
- public void onError(@NonNull Throwable e) {
- if (callBack != null) {
- callBack.failed();
- }
- }
- @Override
- public void onComplete() {
- }
- });
- }
Observable.just執(zhí)行在子線(xiàn)程中, callBack回調(diào)執(zhí)行在主線(xiàn)程中
基于mockito,我們直接寫(xiě)出對(duì)應(yīng)的單元測(cè)試代碼:
- @Test
- public void threadSwitch() {
- presenter.threadSwitch();
- // 驗(yàn)證callBack的success方法被調(diào)用了5次
- verify(callBack,times(5)).success(anyString());
- }
執(zhí)行此用例,我們會(huì)發(fā)現(xiàn)它會(huì)報(bào)如下錯(cuò)誤:
- java.lang.ExceptionInInitializerError
- at io.reactivex.rxjava3.openharmony.schedulers.OpenHarmonySchedulers.lambda$static$0(Unknown Source)
- at io.reactivex.rxjava3.openharmony.plugins.RxOpenHarmonyPlugins.callRequireNonNull(Unknown Source)
- at io.reactivex.rxjava3.openharmony.plugins.RxOpenHarmonyPlugins.initMainThreadScheduler(Unknown Source)
- at io.reactivex.rxjava3.openharmony.schedulers.OpenHarmonySchedulers.<clinit>(Unknown Source)
- at kale.ui.shatter.test.RxSchedulerPresenter.threadSwitch(RxSchedulerPresenter.java:65)
- at kale.ui.shatter.test.RxSchedulerTestTest.threadSwitch(RxSchedulerTestTest.java:52)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:498)
- at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
- at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
- at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
- at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
- at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
- at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
- at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
- at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
- at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
- at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
- at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
- at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
- at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
- at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
- at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
- at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
- at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
- at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
- at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
- at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
- at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
- at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
- at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
- at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
- at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
- at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:498)
- at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:128)
- Caused by: java.lang.RuntimeException: Stub!
- at ohos.eventhandler.EventRunner.getMainEventRunner(EventRunner.java:110)
- at io.reactivex.rxjava3.openharmony.schedulers.OpenHarmonySchedulers$MainHolder.<clinit>(Unknown Source)
- ... 41 more
那么怎么解決呢?那就是設(shè)置用到的Schedulers.進(jìn)行hook,修改用例如下:
- @Test
- public void threadSwitch() {
- RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
- RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
- RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
- RxOpenHarmonyPlugins.setInitMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
- presenter.threadSwitch();
- // 驗(yàn)證callBack的success方法被調(diào)用了5次
- verify(callBack,times(5)).success(anyString());
- }
原理就是當(dāng)進(jìn)行線(xiàn)程調(diào)度時(shí),都讓它切換到Schedulers.trampoline(),這樣我們就能正確的輸出了。但通常情況下,我們使用到線(xiàn)程切換的場(chǎng)景會(huì)很多,這樣寫(xiě)畢竟還是不夠優(yōu)雅,稍后我會(huì)給出更好的解決方式。
除了上面的線(xiàn)程切換場(chǎng)景,我們還經(jīng)常會(huì)使用到時(shí)間輪詢(xún)之類(lèi)的場(chǎng)景,例如:
- public void interval() {
- Observable.interval(1, TimeUnit.SECONDS)
- .take(5)
- .flatMap((Function<Long, ObservableSource<String>>)
- aLong -> Observable.just(aLong + ""))
- .subscribeOn(Schedulers.newThread())
- .observeOn(OpenHarmonySchedulers.mainThread())
- .subscribe(new Observer<String>() {
- @Override
- public void onSubscribe(@NonNull Disposable d) {
- }
- @Override
- public void onNext(@NonNull String s) {
- System.out.println(s);
- if (callBack != null) {
- callBack.success(s);
- }
- }
- @Override
- public void onError(@NonNull Throwable e) {
- if (callBack != null) {
- callBack.failed();
- }
- }
- @Override
- public void onComplete() {
- }
- });
- }
我們每隔1秒發(fā)射一次數(shù)據(jù),一共發(fā)送5次,我們寫(xiě)出以下單元測(cè)試:
- @Test
- public void interval() {
- RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
- RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
- RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
- RxOpenHarmonyPlugins.setInitMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
- presenter.interval();
- // 驗(yàn)證callBack的success方法被調(diào)用了5次
- verify(callBack,times(5)).success(anyString());
- }
使用上面線(xiàn)程異步變同步的方法確實(shí)可以進(jìn)行測(cè)試,但是需要等到5秒后才能執(zhí)行完成,這顯然不符合單元測(cè)試執(zhí)行快的特點(diǎn)。這里,RxJava給我們提供了TestScheduler,調(diào)用TestScheduler的advanceTimeTo或advanceTimeBy方法來(lái)進(jìn)行時(shí)間操作。
- @Test
- public void interval() {
- TestScheduler testScheduler = new TestScheduler();
- RxJavaPlugins.setIoSchedulerHandler(scheduler -> testScheduler);
- RxJavaPlugins.setComputationSchedulerHandler(scheduler -> testScheduler);
- RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> testScheduler);
- RxOpenHarmonyPlugins.setInitMainThreadSchedulerHandler(scheduler -> testScheduler);
- presenter.interval();
- //將時(shí)間設(shè)到3秒后
- testScheduler.advanceTimeTo(3,TimeUnit.SECONDS);
- verify(callBack,times(3)).success(anyString());
- //將時(shí)間設(shè)到10秒后
- testScheduler.advanceTimeTo(10,TimeUnit.SECONDS);
- verify(callBack,times(5)).success(anyString());
- }
這樣我們就不用每次執(zhí)行到該用例的時(shí)候,還得等待設(shè)定的時(shí)間。每次這樣寫(xiě)畢竟也不夠優(yōu)雅,下面我給出基于rxjava3和Rxohos:1.0.0,使用TestRule來(lái)進(jìn)行RxJava線(xiàn)程切換及時(shí)間操作的工具類(lèi),供大家參考:
- /**
- * Created by xiongwg on 2021-07-08.
- * <p>
- * 這個(gè)類(lèi)是讓Obserable從異步變同步。
- *
- * 注意: 當(dāng)有操作時(shí)間的測(cè)試時(shí),必須調(diào)用{@link #setScheduler(Scheduler)}方法
- */
- public class RxJavaTestSchedulerRule implements TestRule {
- /**
- * 運(yùn)行在當(dāng)前線(xiàn)程,可異步變同步
- */
- public static final Scheduler DEFAULT_SCHEDULER = Schedulers.trampoline();
- /**
- * 操作時(shí)間類(lèi)的 Scheduler
- */
- public static final Scheduler TIME_SCHEDULER = new TestScheduler();
- private Scheduler mScheduler = DEFAULT_SCHEDULER;
- /**
- * 切換 Scheduler
- *
- * @param scheduler 單元測(cè)試用例執(zhí)行所在的 Scheduler
- */
- public void setScheduler(Scheduler scheduler) {
- if (scheduler != mScheduler) {
- mScheduler = scheduler;
- resetTestSchecduler();
- }
- }
- @Override
- public Statement apply(final Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- resetTestSchecduler();
- base.evaluate();
- }
- };
- }
- public void advanceTimeBy(long delayTime, TimeUnit unit) {
- if (mScheduler instanceof TestScheduler) {
- ((TestScheduler) mScheduler).advanceTimeBy(delayTime, unit);
- }
- }
- public void advanceTimeTo(long delayTime, TimeUnit unit) {
- if (mScheduler instanceof TestScheduler) {
- ((TestScheduler) mScheduler).advanceTimeTo(delayTime, unit);
- }
- }
- public void triggerActions() {
- if (mScheduler instanceof TestScheduler) {
- ((TestScheduler) mScheduler).triggerActions();
- }
- }
- private void resetTestSchecduler() {
- RxJavaPlugins.reset();
- RxJavaPlugins.setIoSchedulerHandler(scheduler -> mScheduler);
- RxJavaPlugins.setComputationSchedulerHandler(scheduler -> mScheduler);
- RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> mScheduler);
- RxOpenHarmonyPlugins.reset();
- RxOpenHarmonyPlugins.setInitMainThreadSchedulerHandler(scheduler -> mScheduler);
- }
使用起來(lái)很簡(jiǎn)單
- // 1、聲明RxJavaTestSchedulerRule Rule
- @Rule
- public RxJavaTestSchedulerRule rxJavaTestSchedulerRule = new RxJavaTestSchedulerRule();
- @Test
- public void interval() {
- //2、在需要進(jìn)行時(shí)間操作的方法前,設(shè)置Scheduler為T(mén)IME_SCHEDULER
- rxJavaTestSchedulerRule.setScheduler(TIME_SCHEDULER);
- presenter.interval();
- //3、操作時(shí)間,將時(shí)間設(shè)置為3秒后
- rxJavaTestSchedulerRule.advanceTimeTo(3, TimeUnit.SECONDS);
- verify(callBack,times(3)).success(anyString());
- //將時(shí)間設(shè)置為10秒后
- rxJavaTestSchedulerRule.advanceTimeTo(10, TimeUnit.SECONDS);
- verify(callBack,times(5)).success(anyString());
- }
七、其它
Java單元測(cè)試中引入了ohos相關(guān)類(lèi)的解決方案
1、嘗試Mock出該對(duì)象
2、在java單元測(cè)試包下新建同包名同類(lèi)名的Java文件,重寫(xiě)調(diào)用到的方法
項(xiàng)目本地查看測(cè)試覆蓋率
右擊需要測(cè)試覆蓋率的包名 ==> 點(diǎn)擊“run test in ‘xxx’ with Coverage”

項(xiàng)目本地查看測(cè)試案例通過(guò)率



想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)