我学习如何测试Android中MVP体系结构的演示者层,我的演示者使用Raturofit 2,在我的活动中,我使用dagger2作为对我的演示者的依赖性注入,这是我的dagger和dagger和主持人注入看起来像:
@Inject
AddScreenPresenter addScreenPresenter;
这是dagger构建器:
DaggerAddScreenComponent.builder()
.netComponent(((App) getApplicationContext()).getNetComponent())
.addScreenModule(new AddScreenModule(this, new ContactDatabaseHelper(this)))
.build().inject(this);
这是我的主持人构造函数:
@Inject
public AddScreenPresenter(Retrofit retrofit, AddScreenContact.View view, ContactDatabaseHelper contactDatabaseHelper)
{
this.retrofit = retrofit;
this.view = view;
this.contactDatabaseHelper = contactDatabaseHelper;
}
我已经写了单元测试类并模拟了改造类,但是当我运行时,出现错误:
Mockito cannot mock/spy following:
- 最后一堂课 - 匿名类 - 原始类型
这是测试类:
@RunWith(MockitoJUnitRunner.class)
public class AddScreenPresenterTest {
private AddScreenPresenter mAddPresenter;
@Mock
private Retrofit mRetrofit;
@Mock
private Context mContext;
@Mock
private AddScreenContact.View mView;
@Mock
private ContactDatabaseHelper mContactDatabaseHelper;
String firstName, phoneNumber;
Upload upload;
@Before
public void setup() {
mAddPresenter = new AddScreenPresenter(mRetrofit, mView, mContactDatabaseHelper);
firstName = "aFirstName";
phoneNumber = "998012341234";
Uri path = Uri.parse("android.resource://"+BuildConfig.APPLICATION_ID+"/" + R.drawable.missing);
upload = new Upload();
upload.title = firstName;
upload.description = "aDescription";
upload.albumId = "XXXXX";
upload.image = new File(path.getPath());
}
@Test
public void checkValidationTest() {
verify(mAddPresenter).checkValidation(firstName, phoneNumber);
}
@Test
public void uploadMultiPartTest() {
verify(mAddPresenter).uploadMultiPart(upload);
}
}
这是我的模块:
@Module
public class AddScreenModule {
private final AddScreenContact.View mView;
private final ContactDatabaseHelper mContactDatabaseHelper;
public AddScreenModule (AddScreenContact.View view, ContactDatabaseHelper contactDatabaseHelper)
{
this.mView = view;
this.mContactDatabaseHelper = contactDatabaseHelper;
}
@Provides
@CustomScope
AddScreenContact.View providesAddScreenContactView() {
return mView;
}
@Provides
@CustomScope
ContactDatabaseHelper providesContactDatabaseHelper() {
return mContactDatabaseHelper;
}
}
我知道改造课是最后一类,现在我坚持下去,不知道如何在测试类中创建演示者对象。请帮助我,如何在构造函数中使用Raterrofit创建演示者类的对象。随时询问我的问题是否还不够清楚,并非常感谢您的帮助。
我个人会让主持人不依赖Retrofit
类,而是对Retrofit
创建的服务 - 这些都是模拟的。
很难从您发布的代码中说出您的演示者实际使用的服务,但是为了简单起见,可以说它仅使用一个服务,也可以说是AddsService
-这是一个可以使用Retrofit的接口。这样的东西例如
public interface AddsService {
@GET(...)
Call<List<Adds>> getAllAdds();
}
现在,您可以使您的主持人取决于此而不是Retrofit
@Inject
public AddScreenPresenter(AddsService addsService,
AddScreenContact.View view,
ContactDatabaseHelper contactDatabaseHelper){
this.addsService = addsService;
this.view = view;
this.contactDatabaseHelper = contactDatabaseHelper;
}
您现在需要提供此依赖性。我猜您也有NetModule
,因为您有NetComponent
,所以我认为您可以做:
@Module
public class NetModule {
// Methods providing Retrofit
@Provides
@Singleton
public AddsService providesAddsService(Retrofit retrofit) {
return retrofit.create(AddsService.class);
}
}
请注意providesAddsService
是如何依赖改造的?由于您的演示者取决于它,因此应该已经提供了这一点。您不需要为此更改任何东西。dagger能够弄清楚如何向方法providesAddsService
提供Retrofit
。
请注意,我假设您可以在Singleton
范围内提供这些。我认为这是因为在您的代码中您从应用程序中检索组件,该组件应处理单例范围。
现在在测试中您可以简单地模拟AddsService
并测试您的演示者。
如果您的演示者依靠更多的服务,我也会将它们传递到构造函数中,并提供dagger。
作为奖励,让我还说改造实例和改造服务只能创建一次(或至少少于尽可能少的次数)。这是因为它们通常是昂贵的操作,您通常总是用不同的参数查询相同的端点。
编辑
在评论中回答一些问题。首先是简单的:如何在测试类中创建主持人?像您一样,我也尝试在测试期间摆脱dagger,这就是为什么我更喜欢构造函数依赖注入,就像您显示所使用的一样。因此,在我的测试课上,我的一些非常相似的东西与您:
@RunWith(MockitoJUnitRunner.class)
public class AddScreenPresenterTest {
private AddScreenPresenter mAddPresenter;
@Mock
private AddsService addsService;
// ...
@Before
public void setUp() throws Exception {
mAddPresenter = new AddScreenPresenter(addsService,
mView, mContactDatabaseHelper);
// ...
}
}
基本上唯一的区别是我将模拟传递给服务。
现在是第二个问题:如何从活动中调用主持人构造函数?好吧,你没有...这是依赖注入的整个想法。您应该使用dagger为主持人提供。我认为这已经是您所做的,我想这就是您的活动中的意思:
@Inject
AddScreenPresenter addScreenPresenter;
因此,您需要做的就是在模块中使用提供者的方法,并能够注入它。
您还可以使组件返回模块提供的演示者:
@Component(...)
public interface AddScreenComponent {
AddScreenPresenter getPresenter();
}
然后在您的活动中,您会做类似的事情:
addScreenPresenter = component.getPresenter();
我在这里真的没有任何偏好。关键是要了解您不应自己构建对象(除非在@Module
s内部)。每当您看到使用new
时,经过经验,这意味着您对该对象有严格的依赖性,并且应该提取它要被注入。因此,这就是为什么您应该避免在活动中创建主持人的原因。它将将演示者与活动相结合。