我学习如何在android中测试MVP架构的演示者层,我的演示者使用改装2,在我的活动中我使用dagger 2作为依赖注入到我的演示者,这是我的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;
}
我编写了单元测试类并模拟了Retrofit类,但是当我运行它时,出现错误:
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;
}
}
我知道Retrofit类是最后一个类,现在我卡住了,不知道如何在我的测试类中创建presenter对象。请帮助我,如何在构造函数中使用retrofit创建presenter类的对象。请随意询问我的问题是否不够明确,非常感谢您的帮助。
答案 0 :(得分:6)
我个人认为主持人不依赖于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能够弄清楚如何向方法Retrofit
提供providesAddsService
。
另请注意,我假设您可以在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);
// ...
}
}
所以基本上唯一的区别是我会将模拟传递给服务。
现在第二个问题:如何从活动中调用presenter构造函数?嗯,你不...那就是依赖注入的整个想法。您应该使用匕首来提供演示者。我认为这已经是你做的了,我想这就是你活动中的内容:
@Inject
AddScreenPresenter addScreenPresenter;
所以你需要做的就是在你的模块中有一个提供者的方法来提供这个,并且能够注入它。
您还可以让组件返回模块提供的演示者:
@Component(...)
public interface AddScreenComponent {
AddScreenPresenter getPresenter();
}
然后在你的活动中你会做类似的事情:
addScreenPresenter = component.getPresenter();
我在这里没有任何偏好。关键是要明白你不应该自己构建对象(除非在@Module
内)。根据经验,任何时候你看到使用new
意味着你对该对象有严格的依赖,你应该提取它以便注入。所以这就是为什么你应该避免在你的活动中创建演示者。它会将演示者与活动结合起来。