Android Mocking Dagger2为Espresso测试注入依赖项

时间:2017-03-29 23:50:16

标签: android mockito android-espresso dagger-2

我有一个严重的依赖注入( dagger2 )应用程序。我想在没有测试导航整个应用程序的情况下运行 espresso测试,然后登录到应用程序。

我想从我的teleActivity开始,并模拟登录管理器。但是在任何@test函数中,我们已经调用了空指针,因为我们调用了onCreate。如果我在启动活动之前覆盖它(如下所示),则活动为空。

根据我的理解,切换下划线依赖关系的能力是我们使用Dagger2的一个重要原因,否则它只是一个非常过度设计的单例。如何覆盖,模拟或切换注射到测试匕首模块 - 所以我可以创建这个简单的浓咖啡测试。

注意我还在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 Injector

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

LoginStateManagerModule

@Module
public class LoginStateManagerModule {

    @Provides
    @Singleton
    public LoginStateManager getLoginStateManager(){
        return new LoginStateManager();
    }
}

3 个答案:

答案 0 :(得分:5)

首先,您决定使用依赖注入(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

答案 1 :(得分:0)

似乎是建筑问题,而不是一个小问题。

首先,我不会为调用dagger2组件创建一个静态类,我的方法将更加以android为中心,我的意思是使用单例应用程序及其所有的铃声和​​口哨。

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

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

使用企业架构的2-Logic模块说MVP / MVC / MVVM(它应该是你android工作室内的一个不同的项目)

你应该在哪里使用匕首?在您的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

答案 2 :(得分:0)

中没有设置值
  

LoginStateManager

因此,当您构建组件时,您将获得 TelePresenter依赖关系和LoginStateManager依赖关系 但是没有在两者的成员变量中设置值。所以我认为你需要在访问成员变量之前设置它们的值。

{{1}}

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