【问题标题】:Mockito Android Unit testingMockito Android 单元测试
【发布时间】:2017-03-29 12:06:43
【问题描述】:

我的应用程序中有 MVP。 Presenter有接口

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

实现有RX Single

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

我还有 Dagger 的模块

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

所以使用 Dagger 我需要正确构建 Presenter。为此,我正在尝试使用 Mockito.when。首先看起来这条线不起作用

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

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

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

【问题讨论】:

  • 不起作用是什么意思?你的意思是它没有返回你的模拟?你确定 dagger 用参数null 调用provideLoginModel 吗?如果您不关心参数,请执行Mockito.when(mockModel.provideLoginModel(any())).thenReturn(mock);
  • 谢谢any() - 是我其他几个问题的答案
  • 太好了,我更新了答案以帮助未来的读者。

标签: android unit-testing mockito dagger-2


【解决方案1】:

如何从您的生产环境中创建一个测试模块 Module

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

@Module
public class ModelModuleTest extends ModelModule {

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

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

【讨论】:

  • 是的,我这样做了,但实际上我的模块应该完全覆盖原来的模块,因为 Dagger 会以任何方式创建 LoginModel。然后我返回存根一个
  • 什么问题?
【解决方案2】:

更新

问题可能是你在嘲笑将null。在这种情况下,mockModel在使用 null

调用 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

大图 对于单元测试,我建议不要使用 Dagger,而是使用 @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();
    }
}

如果您正在进行集成测试并且需要多个真实对象,您可以为返回所需对象的组件创建一个内部/匿名模块。 (而不是试图模拟模块接口)。

【讨论】:

  • 好的,但是演示者使用依赖项。 Mockito 如何知道要添加什么?
  • @InjectMocks 将使用类型和/或名称来提供构造函数参数和@Inject/@Autowire 注释成员。将 SomePresenterDependency 更改为您通过 Dagger 提供的依赖项。
  • 我的问题更多关于为什么已经创建的模拟不能像那样修改 Mockito.when(RuntimeEnvironment.application.getString(R.string.check_credentials)).thenReturn("bla");跨度>
  • @YevgenKulik RuntimeEnvironment.application 不是 Mockito 模拟,而是 Robolectric 假货。所以你不能用Mockito.when() 存根它
  • 如何likeRuntimeEnvironment.application = spy(RuntimeEnvironment.application) 然后用什么时候重新定义呢?还是我没有正确理解间谍?
猜你喜欢
  • 2020-02-22
  • 1970-01-01
  • 2020-05-03
  • 1970-01-01
  • 2015-07-10
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
相关资源
最近更新 更多