2017-03-29 152 views
1

我在我的应用程序中有MVP。主持人有接口Mockito Android单元测试

public interface ILoginPresenter<V> extends Presenter<V> { 
    void logUserIn(String email, String password, String deviceToken, String deviceType); 
} 

实现具有RX单

mLoginSubscription = mModel.logUserIn(email, password, deviceToken, deviceType) 
      .compose(RxUtil.setupNetworkSingle()) 
      .subscribe(new Subscriber<User>() { 
       @Override 
       public void onCompleted() { 
        Timber.i("Log in complete"); 
       } 

       @Override 
       public void onError(Throwable e) { 
        Timber.e(e, "Retrofit could not get User."); 

        getView().dismissProgressDialog(); 
       } 

       @Override 
       public void onNext(UserResponseRetrofit response) { 
        Timber.i("Retrofit is attempting to get User"); 
        mSaveModel.saveUser(user); 
        getView().dismissProgressDialog(); 
        getView().goToMenuActivity(); 
       } 
      }); 

我也有模块匕首

@Module 
public class ModelModule { 
    @Provides 
    @ScreenScope 
    public ILoginModel provideLoginModel(LoginModel p) { 
     return p; 
    } 
} 

我的单元测试样子未来:

@RunWith(RobolectricTestRunner.class) 
@Config(constants = BuildConfig.class, sdk = 21, manifest = "/src/main/AndroidManifest.xml") 
public class LoginPresenterTest { 

    public static final String SOME_OTHER_TOKEN = "someOtherToken"; 
    private AppComponent mAppComponent; 
    private LoginComponent mLoginComponent; 
    private ILoginView mockView; 
    private ModelModule mockModel; 
    private ILoginPresenter mLoginPresenter; 

    @Before 
    public void setup() { 
     // Creating the mocks 
     mockView = Mockito.mock(ILoginView.class); 
     mockModel = Mockito.mock(ModelModule.class); 

     ILoginModel mock = Mockito.mock(ILoginModel.class); 
     User urr = Mockito.mock(User.class); 
     Mockito.when(mockModel.provideLoginModel(null)).thenReturn(mock); 
     Mockito.when(mock.logUserIn("", "", "", "")).thenReturn(ScalarSynchronousSingle.just(urr)); 

     mAppComponent = DaggerAppComponent.builder() 
       .appModule(new AppModule(RuntimeEnvironment.application)) 
       .build(); 

     mLoginComponent = DaggerLoginComponent.builder() 
       .appComponent(mAppComponent) 
       .modelModule(mockModel) 
       .presenterModule(new PresenterModule()) 
       .build(); 

     mLoginPresenter = mLoginComponent.provideLoginPresenter(); 
     mLoginPresenter.setView(mockView); 
    } 

    @Test 
    public void testLogin() { 
     mLoginPresenter.logUserIn("", "", "", ""); 
     try { 
      java.lang.Thread.sleep(20000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     Mockito.verify(mockView).dismissProgressDialog(); 
    } 

因此,使用匕首我需要正确构建Presenter。为此,我试图使用Mockito.when。首先看起来这条线不起作用

Mockito.when(mockModel.provideLoginModel(null)).thenReturn(mock); 

目标的目的是用我自己的Model实现返回Single。

真的不明白为什么我的ModelModule模拟不起作用?

+1

你是什么意思不起作用?你的意思是它没有回归你的模拟?你确定匕首正在用参数'null'调用'provideLoginModel'吗?如果你不关心这个参数,请执行'Mockito.when(mockModel.provideLoginModel(any()))。然后返回(模拟);' – cyroxis

+1

谢谢'any()' - 对于其他几个问题的答案 –

+0

Great I更新我的答案,以帮助未来的读者。 – cyroxis

回答

0

修订

问题可能是您正在嘲讽的意愿。在这种情况下mockModel会当provideLoginModel被调用

Mockito.when(mockModel.provideLoginModel(null)).thenReturn(mock); 
mockModel.provideLoginModel(null) // returns mock 
mockModel.provideLoginModel(new Foo()) // returns null 

相反,你可以使用一个匹配,如any()只有返回模拟:

Mockito.when(mockModel.provideLoginModel(any())).thenReturn(mock); 
mockModel.provideLoginModel(null) // returns mock 
mockModel.provideLoginModel(new Foo()) // also returns null 

在任何调用返回mock

BIG PICTURE 进行单元测试,我不会用匕首建议,改用@Mock@InjectMocks你只需要你正在测试是真实的,其余可嘲笑的对象。

@RunWith(RobolectricTestRunner.class) 
@Config(constants = BuildConfig.class, sdk = 21, manifest = "/src/main/AndroidManifest.xml") 
public class LoginPresenterTest { 

    public static final String SOME_OTHER_TOKEN = "someOtherToken"; 

    @Mock 
    ILoginView mockView; 

    @Mock 
    SomePresenterDependency somePresenterDependency 

    @InjectMocks 
    ILoginPresenter mLoginPresenter; 

    @Before 
    public void setup() { 
     MockitoAnnotations.injectMocks(this); 

     mLoginPresenter.setView(mockView); 
    } 

    @Test 
    public void testLogin() { 
     mLoginPresenter.logUserIn("", "", "", ""); 
     try { 
      java.lang.Thread.sleep(20000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     Mockito.verify(mockView).dismissProgressDialog(); 
    } 
} 

如果你正在做集成测试和需要多个真实物体你可以只为您的组件返回所需的对象内/匿名模块。 (而不是试图模拟模块接口)。

+0

好的,但演示者使用依赖关系。 Mockito如何知道要添加什么? –

+0

[@InjectMocks](https://static.javadoc.io/org.mockito/mockito-core/2.7.19/org/mockito/InjectMocks.html)将使用类型和/或名称来提供构造函数参数& @ Inject' /'@ Autowire'带注释的成员。将'SomePresenterDependency'更改为您通过Dagger提供的依赖项。 – cyroxis

+0

我的问题更多关于为什么已经创建的模拟不能像那样修改Mockito.when(RuntimeEnvironment.application.getString(R.string.check_credentials))。thenReturn(“bla”); –

1

如何在生产中创建测试模块Module

看他们如何建议通过Dagger in official site进行测试。

@Module 
public class ModelModuleTest extends ModelModule { 

    @Override 
    public ILoginModel provideLoginModel(LoginModel p) { 
     ... 
    } 
} 

您可以将模拟的依赖关系传递给您的Module

+0

是的,我这样做了,但实际上我的模块应该完全覆盖原始模块,因为Dagger将以任何方式创建LoginModel。然后我返回一个存根 –

+1

什么问题? – azizbekian