你應該知道的android單元測試

介紹

  • setup:即new 出待測試的類,設置一些前提條件

  • 執行動作:即調用被測類的被測方法,並獲取返回結果

  • 驗證結果:驗證獲取的結果跟預期的結果是一樣的

Junit4

java測試框架

testCompile ‘junit:junit:4.12’
@Before
@Test
(expected = IllegalArgumentException.class) 驗證是否拋該異常
@After
@Ignore 跑所有測試方法忽略該方法

除了在AndroidStudio裏面運行,你還可以在命令行通過gradle testDebugUnitTest,或者是gradle testReleaseUnitTest,分別運行debug和release版本的unit testing

Mockito的

  • java界使用最廣法的mock框架
    概念就是創建一個類的虛假對象,在測試環境中,用來替換掉真實的

  • 添加依賴

repositories { jcenter() }
dependencies {
      testCompile "org.mockito:mockito-core:
}
  • api

    1. 驗證方法調用:
      Mockito.verify(objectToVerify,Mockito.times(3)).methodToVerify(Mockito.anyString());

    2. 設定返回值,方便測試:
      Mockito.when(mockObject.targetMethod(args)).thenReturn(desiredReturnValue);

    3. 執行特定動作,如訪問網絡數據
    Mockito.doAnswer(desiredAnswer).when(mockObject).targetMethod(args);
    Mockito.doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        //這裏可以獲得傳給performLogin的參數
        Object[] arguments = invocation.
        //callback是第三個參數
        NetworkCallback callback = (NetworkCallback) arguments[
    
        callback.onFailure(500, "Server error");
         return 500;
        }
    }).when(mockUserManager).performLogin(anyString(), anyString(), any(NetworkCallback.class));

    ps: Mockito.spy與mock的唯一區別就是默認行爲不一樣:spy對象的方法默認調用真實的邏輯,mock對象的方法默認什麼都不做,或直接返回默認值。

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'
    }
}

...
```
//應用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'
}
``````
  • 依賴注入概念
    我們平常使用的構造函數,需要傳入對象去使用,就算是一種依賴注入,然而當我們需要的對象嵌套越來越多的時候,就會顯得冗餘,這個時候dagger2就出現了。

    client –> Component(工廠管理員)–>Module(生產依賴的工廠)–>Dependency

  • api

    Module 
           @Module 修飾類
           @Provides 修飾方法
           @Singleton 修飾方法,獲取單例節省資源
    Component
          @Component
                (modules={OneModule.class})  修飾類,指定管理的生產Dependency的工廠
          1. 定義返回Dependency的抽象方法即可
          2. 先在Component 定義inject(Object obj方法
             定義成員變量@Inject修飾即可
          ps: 不能繼承,都要實現inject(this)方法才能生成實例
    
  • 單元測試

//需要測試
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相關,看不懂的話忽略
@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就是一個能夠讓我們在JVM上跑測試時夠調用安卓的類的框架
- android.jar不完整,android相關方法只是拋出異常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);  
    }  
}  
```
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章