Android 開發之單元測試

Android中的測試主要基於JUnit測試框架,主要有兩種類型:

  • 本地單元測試(Local unit tests): 位於 app/src/test/java/, 直接在本地JVM上運行,不用依賴於Android中間層的API。倘若單元測試時需要用到Android Framework層的特定模塊,可以使用Mockito測試框架來代替這些模塊對象,由此一方面可以將代碼與Android系統本身隔離開,一方面又可以方便的使用Android的依賴對象
  • 工具化測試(Instrumented tests): 位於 app/src/androidTest/java/,該測試直接嵌入APK,因此必須在Android設備或者模擬器上運行。測試,代碼與APP運行在相同的進程中,因此測試代碼可以調用APP的函數或者修改APP中的值域,並且可以實現與APP UI的自動化交互

Android測試原理圖:

上述兩種類型只是爲了區分測試用例運行地方(一種在本地JVM上運行,一種在模擬器或者實機上運行),而在實際寫測試用例時,可以參考下表來構建測試(TestSuite):

類型 子類型 說明
unit Tests(單元測試) Local Unit Tests(本地單元測試) 在本地JVM上運行的單元測試。如果測試用例沒有依賴FW或者可以通過mock的辦法模擬FW的依賴時,可以用該類型的測試以減少測試執行時間
Instrumented Unit Tests(工具化單元測試) 在測試或者模擬器上運行的單元測試用例,此類型測試用例可以訪問Instrumentation類。當mock方法無法模擬Android FW的依賴時,採用這種方法
Integration Tests(集成測試) Components within your app only(僅限於該APP) 該類型測試用於UI測試,可以驗證與用戶交互時APP是否運行正確。常見的UI測試框架 Espresso 可用於模擬用戶的輸入與其他UI交互動作
Cross-app Components(跨APP組件) 用於UI測試,可以驗證APP與其他APP或者系統APP之間的交互。比如,用戶驗證設置Android 設置菜單時,APP是否正常運行。常見的 跨APP UI測試框架有 UI Automator

JUnit Annotations

在JUnit4 測試類中, 主要有如下幾種註解來處理測試代碼:

  • @Before: 用於測試配置,在每個測試運行之前調用;每個測試用例可包含多個@before註解,但是執行順序不定。
  • @After: 與@Before對應,這個註解指定代碼在測試運行完成之後執行,同樣每個測試用例可以有多個@After註解。一般用於釋放佔用的資源。
  • @Test: 用於標記測試方法,單個測試用例可包含多個測試方法。
  • @Rule: 該註解允許用戶靈活添加、重定義每個測試方法的行爲,一般配合Anroid 測試支持庫(Android Testing Support Library)提供的rule類一起使用,如ActivityTestRule, ServiceTestRule.
  • @BeforeClass: 在測試運行之前,指定一個測試用例中的static方法僅執行一次,需要比較耗時、消耗內存的操作時該註解派上用場了。
  • @AfterClass: 在測試運行之後,指定測試用例中的static方法執行,與@BeforeClass相對應,該註解主要用於釋放分配的資源。
  • @Test(timeout=): 指定某個測試執行的Timeout,若超時則測試自動失敗。注意這裏的時間單位是ms。

如何構建Local unit tests

配置Android studio

  • 更新 gradle 插件版本至1.1.0-rc1之後(file->project structure

update gradle version

  • app/build.gradle中添加單元測試依賴項:

add dependency

  • 同步項目(Sync your project)

  • 打開“Build variants” 工具窗口(左側邊欄),將 Test artifact更改爲”Unit Tests”

choose test type

編寫單元測試文件

app/src/test/java/對應的模塊目錄下,建立相應的Unit Test文件:

示例

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.runners.MockitoJUnitRunner;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    import org.powermock.modules.junit4.PowerMockRunner;

    import java.util.HashMap;

    import static org.junit.Assert.*;

    /**
     * To work on unit tests, switch the Test Artifact in the Build Variants view.
     */

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(Log.class)
    public class HtmlParserUnitTest {
    @Test
    public void htmlParser_isCorrect() throws Exception {
        String html = "<div class=\"info\" id=\"info\"><dl><dt><img src=\"http://www.liantu.com/tiaoma/eantitle.php?title=U3FlV1I4aGdObXkvRGJiYmphOUVFT0VvVFFnRmc1WWg=\" alt=\"商品名稱\"></dt><dd><span>參考價格:</span><i>2.5</i>元</dd><dd><span>廠商代碼:</span>69475037</dd><dd><span>商品國別:</span>中國</dd><dd><span>廠商名稱:</span>上海晨光文具股份有限公司</dd></dl></div>";

        PowerMockito.mockStatic(Log.class);
        HtmlParserUtil parser = HtmlParserUtil.getInstance();
        parser.setHtml(html);
        HashMap<String,String> result = new HashMap<>();
        result = parser.parseHtmlLianTu();

        assertEquals(result.equals(null),false);
        assertNotEquals(result.size(), 0);
    }
}

右擊Unit Tests對應的 JAVA文件,即可進行測試。 另外也可以通過命令行 gradlew test運行測試用例。

若編寫Unit Test用例時,需要使用Android FW 中的類,則需要使用 mockito 或者 PowerMock 來模擬Android FW的類。 PowerMock在mockito的基礎上做了擴展, PowerMock支持static、private方法的模擬,因而使用起來更爲方便,可參考:
mockito:http://site.mockito.org/mockito/docs/current/overview-summary.html
powermock: https://github.com/robolectric/robolectric/wiki/Using-PowerMock

工具化單元測試

在編寫工具化測試用例之前,需要在 build.gradle 中包含相關的編譯依賴,

androidTestCompile 'com.android.support:support-annotations:24.0.0'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
// for UI testing
androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'

工具化單元測試與本地單元測試的寫法完全一致,兩者的區別在於前者可以使用Android FW層的類,如下寫了一個測試網絡狀態Utility的測試用例:

import android.content.Context;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;

import com.github.jason.storemanager.application.AppGlobals;
import com.github.jason.storemanager.utils.NetworkStateUtil;

import junit.framework.TestCase;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
 * Created by JasonWang on 2016/9/4.
 */

@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkStateUtilTest extends TestCase{

    private Context mContext;

    @Before
    @Override
    public void setUp() throws Exception{
        super.setUp();

        mContext = AppGlobals.getAppContext();

    }

    @Test
    public void networkState_isCorrect(){
        NetworkStateUtil netState = new NetworkStateUtil(mContext);

        assertTrue(netState.isNetworkConnected());
    }

    @Override
    public void tearDown(){
        // release resources here
    }
}

有時,編寫了很多個測試用例,如果一個個去執行測試驗證,會很麻煩,因此Android提供了一個將所有測試用例包含在一個TestSuite類:只要測試用例包含在TestSuite中,運行該TestSuite,所有的測試用例都會被執行。

    import com.example.android.testing.mysample.CalculatorAddParameterizedTest;
    import com.example.android.testing.mysample.CalculatorInstrumentationTest;
    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;

    // Runs all unit tests.
    @RunWith(Suite.class)
    @Suite.SuiteClasses({CalculatorInstrumentationTest.class,
    CalculatorAddParameterizedTest.class})

    public class UnitTestSuite {}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章