android retrofit2,dagger2单元测试

use*_*932 2 android unit-testing mockito dagger-2 retrofit2

我学习如何在android中测试MVP架构的演示者层,我的演示者使用改造2,在我的活动中,我使用dagger 2作为依赖注入到我的演示者,这是我的Dagger和演示者注入看起来像:

@Inject
AddScreenPresenter addScreenPresenter;
Run Code Online (Sandbox Code Playgroud)

这是Dagger的构建者:

DaggerAddScreenComponent.builder()
            .netComponent(((App) getApplicationContext()).getNetComponent())
            .addScreenModule(new AddScreenModule(this, new ContactDatabaseHelper(this)))
            .build().inject(this);
Run Code Online (Sandbox Code Playgroud)

这是我的演示者构造函数:

@Inject
public AddScreenPresenter(Retrofit retrofit, AddScreenContact.View view, ContactDatabaseHelper contactDatabaseHelper)
{
    this.retrofit = retrofit;
    this.view = view;
    this.contactDatabaseHelper = contactDatabaseHelper;
}
Run Code Online (Sandbox Code Playgroud)

我已经编写了单元测试类并模拟了Retrofit类,但是当我运行它时,出现错误:

Mockito cannot mock/spy following:
Run Code Online (Sandbox Code Playgroud)

- 最终类 - 匿名类 - 原始类型

这是测试类:

@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);
}

}
Run Code Online (Sandbox Code Playgroud)

这是我的模块:

@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;
}
}
Run Code Online (Sandbox Code Playgroud)

我知道Retrofit类是最后一个类,现在我卡住了,不知道如何在我的测试类中创建presenter对象.请帮助我,如何在构造函数中使用retrofit创建presenter类的对象.请随意询问我的问题是否不够明确,非常感谢您的帮助.

Fre*_*red 6

就个人而言,我会让演示者不依赖于Retrofit课程,而是依赖于创建的服务Retrofit- 这些都是可以模仿的.

从您发布的演示者实际使用的服务代码中很难说,但为了简单起见,我们假设它只使用一个,让我们说它是AddsService- 这是一个可以使用Retrofit的界面.比如这样的东西

public interface AddsService {
   @GET(...)
   Call<List<Adds>> getAllAdds();
}
Run Code Online (Sandbox Code Playgroud)

现在你可以让你的演示者依赖于此而不是 Retrofit

@Inject
public AddScreenPresenter(AddsService addsService, 
                          AddScreenContact.View view, 
                          ContactDatabaseHelper contactDatabaseHelper){
   this.addsService = addsService;
   this.view = view;
   this.contactDatabaseHelper = contactDatabaseHelper;
}
Run Code Online (Sandbox Code Playgroud)

您现在需要提供此依赖项.我猜你有一个,NetModule因为你有一个NetComponent,所以我假设你可以这样做:

@Module
public class NetModule {
   // Methods providing Retrofit
   @Provides
   @Singleton
   public AddsService providesAddsService(Retrofit retrofit) {
      return retrofit.create(AddsService.class);
   }
}
Run Code Online (Sandbox Code Playgroud)

注意这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);
      // ...
   }
}
Run Code Online (Sandbox Code Playgroud)

所以基本上唯一的区别是我会将模拟传递给服务.

现在第二个问题:如何从活动中调用presenter构造函数?嗯,你不...那就是依赖注入的整个想法.您应该使用匕首来提供演示者.我认为这已经是你做的了,我想这就是你活动中的内容:

@Inject
AddScreenPresenter addScreenPresenter;
Run Code Online (Sandbox Code Playgroud)

因此,您需要做的就是在模块中提供一个提供此方法并且能够注入它的提供程序方法.

您还可以使组件返回模块提供的演示者:

@Component(...)
public interface AddScreenComponent {
   AddScreenPresenter getPresenter();
}
Run Code Online (Sandbox Code Playgroud)

然后在你的活动中,你会做类似的事情:

addScreenPresenter = component.getPresenter();
Run Code Online (Sandbox Code Playgroud)

我在这里没有任何偏好.关键是要明白你不应该自己构建对象(除非在@Modules内).根据经验,任何时候你看到new被使用,这意味着你对该对象有严格的依赖,你应该提取它进行注入.所以这就是为什么你应该避免在你的活动中创建演示者.它会将演示者与活动联系起来.