如何在使用Espresso进行仪器测试时注入Activity的模拟Presenter

13
我已经尝试了一个星期。我爬遍了所有可用的文章,但它们的实现或示例都不足或停留在Espresso测试的步骤上。
我的Android应用程序遵循MVP架构(并且是Java编写的)。
场景:【只给一个例子】 我有一个HomeActivity,它使用Dagger2获取HomePresenter。(在HomeModule中提供方法,并通过HomeComponent中的void inject(HomeActivity activity)公开。)
在我的HomeActivity的espressoTest中,我想注入一个mockpresent。 我没有通过AppComponent将这些依赖项公开到AppModule中,这是网络上大多数示例所做的(因此他们只需创建一个新的testApplication,然后执行必要的操作)
我不想使用productFlavours注入或提供mockclasses,因为它不能让我控制Mockito.when方法。
所以基本上,我想注入一个mockpresenter,在其中我可以为了我的espresso单元测试而进行任何Mockito.when()
以下是我的代码。
组件名称:HomeComponent
@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);
    }

}

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

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

AppModule(应用程序模块)
@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();

再一次。在我的测试中(使用espresso),我想要注入一个由Mockito设置的mockedHomePresenter。这样我就可以单元测试我的视图。
2个回答

8
解决问题的关键是有一个 Dagger Module,在 HomeActivity 的仪表测试中提供模拟 Presenter,而不是“真正”的一个。

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

  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)

为了进行仪器化测试,请创建一个扩展HomeComponentTestHomeComponent:
@HomeScope
@Component(modules = [TestHomeModule::class])
interface TestHomeComponent : HomeComponent

其中TestHomeModule提供了一个模拟的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()
    }
}

2
所以,您的问题是需要创建一个模块,为测试提供一个模拟的演示者而不是“真实”的演示者。
这里有一篇非常好的文章:使用 Dagger 进行测试

嗨,谢谢回复。我知道这篇文章和方法,但我遇到的限制是针对仪器测试。演示者为一个模块提供了解决方案,该模块不需要存在于应用程序组件中。如果我必须创建静态(即硬编码)类/模拟,则Mockito的使用也是多余的。 - yUdoDis

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