Как внедрить фиктивный докладчик активности в инструментальное тестирование с помощью эспрессо

Я пробовал это уже неделю. И я просмотрел все доступные статьи, но их реализации или примеры не оправдывают ожиданий или останавливаются на этапах Espresso Tests.

Мое приложение для Android следует архитектуре MVP (и находится на Java)

Сценарий: [Приведу только один пример] У меня есть HomeActivity, который получает HomePresenter с помощью Dagger2. (Предоставляет метод в HomeModule, представленный через void inject(HomeActivity activity) в HomeComponent.

В моем тесте espressoTest для HomeActivity я хотел бы ввести имитацию презентации. Я не раскрыл эти зависимости внутри AppModule через AppComponent. которые делают большинство примеров в сети (поэтому они просто создают новое testApplication, а затем делают необходимое)

Я не хочу использовать способ productFlavours для внедрения или предоставления имитационных классов, поскольку он не дает мне контроля над Mockito.when методами.

Так что в основном. Я хотел бы внедрить mockpresenter, в котором я могу делать с ним все Mockito.when()s ради моих модульных тестов в эспрессо.

Мой код ниже.

HomeComponent

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

HomeModule

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

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

HomeActivity

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

Снова. В моих тестах (эспрессо) я хотел бы ввести mockedHomePresenter, установленный Mockito. Так что я могу просто тестировать свои представления.


person yUdoDis    schedule 29.06.2018    source источник


Ответы (2)


Ключевым моментом в решении проблемы является наличие такого Dagger Module, который предоставляет фиктивный Presenter в инструментированном тесте HomeActivity вместо «настоящего».

Для этого необходимо выполнить следующие 2 дополнительных действия (вы также можете увидеть пример ).

  1. Делегируйте создание экземпляра Component HomeActivity некоторой абстракции.
  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

где 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()
    }
}
person Onik    schedule 30.04.2020

Так. Ваша проблема в том, что вам нужно создать модуль, который предоставляет фиктивный презентатор для тестирования вместо «настоящего».

Здесь есть неплохая статья: Тестирование с помощью Dagger

person C B J    schedule 07.07.2018
comment
Привет, спасибо за ответ, я знаю об этой статье и подходе, но ограничение, которое я достиг, касается инструментальных тестов. презентатор для модуля, который не обязательно должен существовать в компоненте приложения. И использование Mockito также излишне, если мне нужно создавать статические (читаемые жестко закодированные) классы / макеты. - person yUdoDis; 08.07.2018