本篇紧接着上一篇的内容,对MVP + Dagger进行单元测试。Dagger的部分可以参看 Dagger2与AndroidInjector
1.相关实现代码
首先添加Dagger所需的依赖:
compile 'com.google.dagger:dagger:2.13'
compile 'com.google.dagger:dagger-android:2.13'
compile 'com.google.dagger:dagger-android-support:2.13'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.13'
例子还是使用登录场景的获取验证码与登录。与上篇重复的代码我就忽略了。
封装Dagger的Avtivity : BaseMVPDaggerActivity
public abstract class BaseMVPDaggerActivity<V extends MvpView, T extends BaseMVPPresenter<V>> extends DaggerAppCompatActivity implements MvpView {
@Inject
protected T mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter.attachView((V)this);
mProgress = new ProgressDialog(this);
}
...
}
BaseMVPPresenter
与MvpView
不变。
GithubApi
的初始化。
@Module
public class ClientModule {
@Singleton
@Provides
GithubApi provideGithubApi(Retrofit retrofit){
return retrofit.create(GithubApi.class);
}
@Singleton
@Provides
Retrofit provideRetrofit(Retrofit.Builder builder, OkHttpClient client) {
return builder
.baseUrl(HttpUrl.parse(GithubApi.BASE_URL))
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
@Singleton
@Provides
OkHttpClient provideClient(OkHttpClient.Builder okHttpClient, LoggingInterceptor loggingInterceptor) {
OkHttpClient.Builder builder = okHttpClient.addInterceptor(loggingInterceptor);
return builder.build();
}
/** * 打印信息的拦截器 * @return 拦截器 */
@Singleton
@Provides
LoggingInterceptor provideLoggingInterceptor() {
return new LoggingInterceptor();
}
@Singleton
@Provides
Retrofit.Builder provideRetrofitBuilder() {
return new Retrofit.Builder();
}
@Singleton
@Provides
OkHttpClient.Builder provideClientBuilder() {
return new OkHttpClient.Builder();
}
}
有了ClientModule
,我们就可以轻松地获取到GithubApi
来随时随地的调用网络接口了。
自定义MyApp
继承DaggerApplication
。
public class MyApp extends DaggerApplication {
private static MyApp instance;
@Inject
GithubApi mGithubApi;
@Override
public void onCreate() {
super.onCreate();
if (instance == null){
instance = this;
}
}
public static MyApp getInstance() {
return instance;
}
//直接获取,便于测试
public GithubApi getGithubApi(){
return mGithubApi;
}
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().create(this);
}
}
如果你不想在自定义Application中写这些便于测试的get方法,可以单独写出来,使用Robolectric
的@Config
来单独指定自定义Application。
LoginDaggerActivity
基本没有变化,只是不用初始化LoginDaggerPresenter
了,可以直接使用了。因为Dagger已经帮我们自动创建好了。
public class LoginDaggerActivity extends BaseMVPDaggerActivity<LoginMvpView, LoginDaggerPresenter> implements LoginMvpView, View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mPresenter.getIdentify(); //<--直接调用
....
}
....其余不变
}
LoginDaggerPresenter
变化也不大。只需要构造方法注入,Dagger就会自动帮我们注入我们需要的GithubApi
。实际中,对于SP、数据库的操作都可以这样去写。
@ActivityScope
public class LoginDaggerPresenter extends BaseMVPPresenter<LoginMvpView> {
private GithubApi mApi;
@Inject
public LoginDaggerPresenter(GithubApi mApi){
this.mApi = mApi;
}
....其余不变
}
最后AppComponent
中统一注入各个Module。
@Module
public abstract class BuildersModule {
@ActivityScope
@ContributesAndroidInjector
abstract LoginDaggerActivity loginDaggerActivityInjector();
}
@Singleton
@Component(modules = {
AppModule.class,
ClientModule.class,
BuildersModule.class,
AndroidSupportInjectionModule.class})
public interface AppComponent extends AndroidInjector<MyApp> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApp> {}
}
到这里,实现的代码基本就完了。下面我们来测试它。
2.单元测试代码
Activity
的部分不变,有点小变动的主要是Presenter
的测试部分。
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 23)
public class LoginDaggerPresenterTest {
private LoginDaggerPresenter mPresenter;
@Mock
private LoginMvpView mvpView;
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Before
public void setUp(){
ShadowLog.stream = System.out;
//<---实例化Presenter
mPresenter = new LoginDaggerPresenter(MyApp.getInstance().getGithubApi());
mPresenter.attachView(mvpView);
}
....其余不变
}
可以看出来加入了Dagger后的变化并不是很大,测试起来还是很方便的。
3.使用Jacoco
之前有人在github上问我,有没有使用jacoco生成测试报告。其实使用起来并不是很麻烦。
新建一个jacoco.gradle
文件,并输入以下内容:
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.8.0" //指定jacoco的版本
reportsDir = file("$buildDir/JacocoReport") //指定jacoco生成报告的文件夹
}
android {
buildTypes {
debug {
//打开覆盖率统计开关
testCoverageEnabled = true
}
}
}
//依赖于testDebugUnitTest任务
task jacocoTestReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {
group = "reporting" //指定task的分组
reports {
xml.enabled = true //开启xml报告
html.enabled = true //开启html报告
}
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug/com/zl/weilu/androidut", //指定类文件夹
includes: ["**/*Presenter.*"], //包含类的规则,这里我们生成所有Presenter类的测试报告
excludes: ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*']) //排除类的规则
def mainSrc = "${project.projectDir}/src/main/java" //指定源码目录
sourceDirectories = files([mainSrc])
classDirectories = files([debugTree])
executionData = files("${buildDir}/jacoco/testDebugUnitTest.exec") //指定报告数据的路径
}
然后在应用Module下的build.gradle
文件中引用刚新建的gradle
文件:
apply from: 'jacoco.gradle'
sync项目之后可以在任务列表看到新增加的任务:
其中testDebugUnitTest
任务会生成单元测试结果报告,包含xml及html格式分别对应test-results
和reports
文件夹;jacocoTestReport
任务会生成单元测试覆盖率报告,结果存放在jacoco
和JacocoReport
文件夹。以上的文件都在对应Module的build
文件下。
本项目的测试报告如下:
代码覆盖率如下:
详细的内容我们可以在生成的html页面中点击查看。
PS:如果有乱码问题,可以使用gradlew -Dfile.encoding=UTF-8 jacocoTestReport
来解决
使用前:
使用后:
红色表示未测试部分,绿色表示测试部分,还有一种黄色表示部分测试,比如if else的部分分支被执行。
4.参考
本篇的内容不是很多,但是涵盖的东西却是之前的总和。掌握了这些,一般场景下的单元测试你就已经得心应手了。到这里,单元测试的内容基本就结束了。后面我会查漏补缺一下,看有没有遗漏的地方需要补充。就这样了。。。。
所有代码已上传至Github。希望大家多多点赞支持!
今天的文章Android单元测试(八):Dagger与单元测试分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22936.html