Android模拟Dagger2注入的依赖项以进行Espresso测试

9
我有一个严重依赖注入(dagger2)的应用程序。我想在不让测试通过整个应用程序并登录到应用程序的情况下运行espresso测试。
我想要从我的teleActivity开始,并模拟登录管理器。然而,在任何@test函数中,我们已经遇到了空指针,因为我们调用了onCreate。如果在启动活动之前覆盖它(如下所示),则活动为空。
据我所知,切换底层依赖项的能力是我们使用Dagger2的重要原因,否则它只是一个非常过度设计的单例。如何覆盖、模拟或切换注入到测试dagger模块——这样我就可以创建这个简单的espresso测试。
请注意,如果采用MVP设计模式,也会有一些区别。
TeleActivity
@Inject
TelePresenter mTelePresenter;
@Inject
public LoginStateManager mLoginStateManager;

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ButterKnife.bind(this);
    DaggerInjectorTele.get().inject(this);
    mTelePresenter.setTeleDependencies(this);
    Intent intent = getIntent();

    String searchId = null;

    if (intent != null) {
        searchId = intent.getStringExtra(Constants.SEARCH_ID);
       }

    mTelePresenter.onCreateEvent(searchId,
            Helper.makeAuthorizationHeader(
            // CRASH Null pointer
            mLoginStateManager.getBaseLoginResponse().getAccessToken()));

}

浓缩咖啡

@LargeTest
@RunWith(AndroidJUnit4.class)
public class TeleTest {
    @Rule
    public ActivityTestRule<TeleActivity> mActivityTestRule = new ActivityTestRule(
            TeleActivity.class) {
        @Override
        protected void beforeActivityLaunched() {
            super.beforeActivityLaunched();
            TeleActivity teleActivity = (TeleActivity)getActivity();
             //teleActivity NULL!
            teleActivity.mLoginStateManager = mock(LoginStateManager.class);
            LoginResponse loginResponse = mock(LoginResponse.class);
            when(loginResponse.getAccessToken()).thenReturn("1234");
            // Nope here still null


when(teleActivity.mLoginStateManager.getBaseLoginResponse()).thenReturn(loginResponse);

        }
    };

Dagger 注入器

  public class DaggerInjectorTele {
    private static TelePresenterComponent telePresenterComponent =
            DaggerTelePresenterComponent.builder().build();

    public static TelePresenterComponent get() {
        return telePresenterComponent;
    }
}

TelePresenterComponent

@Singleton
@Component(modules = {TelePresenterModule.class,
        LoginStateManagerModule.class})
public interface TelePresenterComponent {
    void inject(TeleActivity activity);
}

TelePresenterModule

@Module
public class TelePresenterModule {

    @Provides
    @Singleton
    public TelePresenter getTelePresenter() {
        return new TelePresenter();
    }
}

登录状态管理模块

@Module
public class LoginStateManagerModule {

    @Provides
    @Singleton
    public LoginStateManager getLoginStateManager(){
        return new LoginStateManager();
    }
}
3个回答

8

首先,您决定使用依赖注入(Dagger2)是一个非常好的决定,确实会使您的测试更容易编写。

您需要覆盖依赖注入配置(模块)并注入一个模拟对象。以下是如何完成的简单示例。

首先,您需要一个模拟对象:

LoginStateManager lsmMock = mock(LoginStateManager.class);

现在重写 DI 配置以使用这个模拟对象:
//Extend your TelePresenterModule, override provider method
public class TestTelePresenterModule extends TelePresenterModule{
    @Override
    public LoginStateManager getLoginStateManager() {
        //simply return the mock here
        return lsmMock;
    }
}

现在开始测试:
@Test
//this is an espresso test
public void withAMock() {
    //build a new Dagger2 component using the test override
    TelePresenterComponent componentWithOverride = DaggerTelePresenterComponent.builder()
            //mind the Test in the class name, see a class above
            .telePresenterModule(new TestTelePresenterModule())
            .build();

    //now we initialize the dependency injector with this new config
    DaggerInjectorTele.set(componentWithOverride);

    mActivityRule.launchActivity(null);

    //verify that injected mock was interacted with
    verify(lsmMock).whatever();
}

示例来源:https://github.com/yuriykulikov/DIComparison/blob/master/app/src/androidTest/java/com/example/yuriy/dependencyinjectioncomparison/Dagger2Test.java


我就快成功了!我看到的其他例子都没有覆盖注入器。我还做了另一件事,我会在我的问题下发布。 - StarWind0

0

看起来这是一个架构问题,而不是一个小问题。

首先,我不会为调用Dagger2组件创建一个静态类,我的方法更加针对Android,即使用单例应用程序和所有附带的功能。

无论如何......在不运行整个工作流程的情况下运行测试的最佳方法是将项目分成两个不同的项目:

1-UI应用程序,您的Android活动和片段等...

2-逻辑模块使用企业架构,例如MVP / MVC / MVVM(它应该是Android Studio中的不同项目)

你应该在哪里使用dagger? 在您的UI应用程序内,以将逻辑模块粘贴到您的UI中。

您如何测试应用程序的不同部分(逻辑模块)? 由于您将逻辑分离到不同的部分中,即使您不再需要Esperesso,也可以更轻松地为它们编写测试。 简单的单元测试Junit和Mockito可以帮助您而无需运行整个工作流程。

请注意,在您的UI应用程序内不应有任何类型的逻辑。

我的观点是干净的架构:https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

我在我的Github上有一个简单的脚手架,你也可以阅读,如果你喜欢的话: https://github.com/vahidhashemi/android_clean_architecture


嗨Mahid,感谢你的回答。但是我担心我的问题不是关于单元测试最佳架构是什么,而是如何为Espresso测试进行模拟。我确实喜欢你建议我使用MVP,因为我的问题中提到我们使用MVP架构。接下来,即使按照您建议的更改,我仍然担心我们仍然有同样的问题。现在我需要模拟一个单例而不是注入的单例。 - StarWind0

0

没有值被设置在

LoginStateManager

所以当你构建这个组件时,你得到了TelePresenter Dependency和LoginStateManager dependency,但是两者的成员变量都没有值被设置。因此,我认为在访问它们之前需要先设置成员变量的值。

getBaseLoginResponse().getAccessToken())

上面的代码行返回 null 是因为你还没有设置值。所以在访问它之前,你需要先设置值。


嗨,Navneet。欢迎加入我们的小组。我建议你仔细阅读问题。你会发现这是一个关于Espresso、Dagger2和Mocking的问题。如果你不熟悉这些主题,那么你将无法回答。经理未设置的事实正是我们试图解决的问题。问题是如何在这种环境下设置这些值。 - StarWind0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接