2017-03-29 200 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