首页 > 解决方案 > 如何在使用 Espresso 的仪器测试中注入 Mocked Presenter of Activity

问题描述

我已经尝试了一个星期。我已经抓取了所有可用的文章,但它们的实现或示例不足或停留在 Espresso 测试的步骤中。

我的 Android 应用程序遵循 MVP 架构(并且在 Java 中)

场景:[仅举一个例子]我有HomeActivity一个HomePresenter使用 Dagger2 的。(HomeModule通过. void inject(HomeActivity activity)_HomeComponent

在我的 espressoTest 中,HomeActivity我想注入一个模拟礼物。我没有AppModule通过AppComponent. 网络上的大多数例子都是这样做的(所以他们只是创建一个新的 testApplication 然后做需要的)

我不想使用 productFlavours 注入或提供模拟类的方式,因为它不能让我控制这些Mockito.when方法。

所以基本上。我想注入一个模拟演示者Mockito.when(),为了我在浓缩咖啡中的单元测试,我可以在其中做任何事情。

我的代码如下。

主页组件

@HomeScope
@Component(modules = HomeModule.class,dependencies = AppComponent.class)
public interface HomeComponent {
    void inject(HomeActivity activity);
}

主页模块

@Module
public class HomeModule {

    private final IHomeContract.View view;

    public HomeModule(IHomeContract.View view) {
        this.view = view;
    }

    @Provides
    @HomeScope
    public IHomeContract.Presenter presenter(FlowsRepository flowsRepository, UserRepository userRepository, LoanRepository loanRepository) {
        return new HomePresenter(view, flowsRepository, userRepository, loanRepository);
    }

}

应用组件

@Component(modules = {AppModule.class,RepositoryModule.class})
@AppScope
public interface AppComponent {
    void inject(App app);

    FlowsRepository flowRepository();
    LoanRepository loanRepository();
    UserRepository userRepository();
}

应用模块

@Module
public class AppModule {
    private Context appContext;

    public AppModule(@NonNull Context context) {
        this.appContext = context;
    }

    @Provides
    @AppScope
    public Context context() {
        return appContext;
    }
}

应用程序

component = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .build();
        component.inject(this);

家活动

HomeComponent component = DaggerHomeComponent.builder()
                .appComponent(((App) getApplication()).getComponent())
                .homeModule(new HomeModule(this))
                .build();

再次。在我的测试(浓缩咖啡)中,我想注入一个由 Mockito 设置的 mockedHomePresenter。所以我可以对我的观点进行单元测试。

标签: androidtdddagger-2android-espressoandroid-mvp

解决方案


解决问题的关键是要有这样一个Dagger 模块,它在的仪器测试中提供一个模拟PresenterHomeActivity而不是“真实”的。

为此,需要执行以下 2 个额外操作(您可能还想查看示例)。

  1. 将HomeActivityComponent的实例化委托给某个抽象。
  2. 用插装测试代替抽象的实现来提供模拟。

我将在下面的示例中使用Kotlin

定义委托接口:

interface HomeComponentBuilder {
    fun build(view: IHomeContract.View): HomeComponent
}

HomeComponent初始化从HomeActivity委托实现移动:

class HomeComponentBuilderImpl constructor(private val app: App) : HomeComponentBuilder {

override fun build(view: IHomeContract.View): HomeComponent =
    DaggerHomeComponent.builder()
        .homeModule(HomeModule(view))
        .build()
}

使委托处于应用程序“范围”中,以便您可以交换其实现以进行检测测试:

interface App {
    val homeComponentBuilder: HomeComponentBuilder
    ...
}

App实施现在应该包含

class AppImpl : Application(), App {
    override val homeComponentBuilder: HomeComponentBuilder by lazy {
        HomeComponentBuilderImpl(this@AppImpl)
    }
    ...
}

组件初始化HomeActivity如下所示:

(application as App)
        .homeComponentBuilder
        .build(this)
        .inject(this)

TestHomeComponent对于扩展的检测测试创建HomeComponent

@HomeScope
@Component(modules = [TestHomeModule::class])
interface TestHomeComponent : HomeComponent

whereTestHomeModule提供了一个模拟Presenter

@Module
class TestHomeModule {

    @Provides
    fun providePresenter(): IHomeContract.Presenter = mock()
}

剩下要做的就是做一个测试委托实现

class TestHomeComponentBuilderImpl : HomeComponentBuilder {
    override fun build(view: IHomeContract.View): HomeComponent =
        DaggerTestHomeComponent.builder()
             .testTestHomeModule(TestHomeModule())
             .build()
}

并将其初始化为TestAppImpl

class TestAppImpl : Application(), App {
    override val homeComponentBuilder: HomeComponentBuilder by lazy {
        TestHomeComponentBuilderImpl()
    }
    ...
}

其余的都是标准的。创建一个AndroidJUnitRunner使用的自定义TestAppImpl

class TestAppRunner : AndroidJUnitRunner() {
    override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application = Instrumentation.newApplication(TestAppImpl::class.java, context)
}

并将其添加到app模块build.gradle

defaultConfig {
    testInstrumentationRunner "your.package.TestAppRunner"
    ...
}

使用示例:

@RunWith(AndroidJUnit4::class)
class HomeActivityTest {
    private lateinit var mockPresenter: IHomeContract.Presenter

    @get:Rule
    val activityRule = ActivityTestRule(HomeActivity::class.java)

    @Before
    fun setUp() {
        mockPresenter = activityRule.activity.presenter
    }

    @Test
    fun activity_onCreate_presenter_should_onViewCreated() {
        verify(mockPresenter).someMethod()
    }
}

推荐阅读