Dagger 2 - вводить поля в активность - PullRequest
0 голосов
/ 29 мая 2020

Перед тем, как начать, я прочитал много руководств, но каждый из них содержит информацию о старом кинжале - с использованием @builder, который теперь устарел. Я использую @Factory

Что у меня есть?

class LoginActivity : AppCompatActivity() {

@Inject
lateinit var authService: AuthService

override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this)
....
}
}
//----------------
@Singleton
@Component(modules = [TestAppModule::class])
interface TestApplicationComponent : AndroidInjector<TestMyApplication> {
    @Component.Factory
    abstract class Builder : AndroidInjector.Factory<TestMyApplication>
}
//----------------
class TestMyApplication : MyApplication() {
    override fun onCreate() {
        super.onCreate()
        JodaTimeAndroid.init(this)
    }
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerTestApplicationComponent.factory().create(this)
    }
}
//----------------
@Singleton
open class AuthService @Inject constructor(
    @AppContext val context: Context, private val authRemoteDataSource: AuthRemoteDataSource
) {
...
}
//----------------
class MockRunner : AndroidJUnitRunner() {
    override fun onCreate(arguments: Bundle?) {
        StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder().permitAll().build())
        super.onCreate(arguments)
    }

    override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
        return super.newApplication(cl, TestMyApplication::class.qualifiedName, context)
    }
}

Примечания :

  1. Я показываю вам, конструктор в AuthService потому что он имеет более 0 аргументов
  2. Mock runner применяет мой TestMyApplication class

и TestClass

@RunWith(AndroidJUnit4::class)
    class LoginActivityTest {

@Mock
lateinit var mockAuthService: AuthService

@Rule
@JvmField
val activityRule = ActivityTestRule<LoginActivity>(LoginActivity::class.java, false, false)

@Before
fun beforeEach() {
    MockitoAnnotations.initMocks(this)
    Mockito.doReturn(NOT_SIGNED).`when`(mockAuthService).getUserSignedStatus(ArgumentMatchers.anyBoolean())
    println(mockAuthService.getUserSignedStatus(true)) //test
}

@Test
fun buttonLogin() {
    activityRule.launchActivity(Intent())
    onView(withText("Google")).check(matches(isDisplayed()));
}
}

Что я хочу? - Проще всего прикрепить имитацию AuthService к LoginActivity

Что у меня? Ошибка:

При вызове метода: android.content.Context.getSharedPreferences В строка:

Mockito.doReturn(NOT_SIGNED).`when`(mockAuthService).getUserSignedStatus(ArgumentMatchers.anyBoolean())

Метод getSharedPreferences вызывается в реальном методе getUserSignedStatus. Итак, теперь , я получаю сообщение об ошибке, потому что Mockito.when вызывает реальную функцию publi c. Думаю, вторая проблема будет заключаться в том, что издевающийся AuthService не вводится в LoginActivity

1 Ответ

1 голос
/ 29 мая 2020

Так что вам, вероятно, следует предоставить AuthService через модуль, один для обычного приложения и один для теста android, который предоставляет имитацию версии. Это означало бы удаление аннотаций Dagger из класса AuthService. Я не использую Component.Factory, но этого примера должно быть достаточно, чтобы вы могли использовать его в качестве руководства.

В папке androidTest:

Создать тестовый модуль:

    // normal app should include the module to supply this dependency
    @Module object AndroidTestModule {

        val mock : AuthService = Mockito.mock(AuthService::class.java)

        @Provides
        @Singleton
        @JvmStatic
        fun mockService() : AuthService =  mock

    }

Создать тестовый компонент:

@Component(modules = [AndroidTestModule::class])
@Singleton
interface AndroidTestComponent : AndroidInjector<AndroidTestApp> {

    @Component.Builder interface Builder {

        @BindsInstance fun app(app : Application) : Builder

        fun build() : AndroidTestComponent
    }
}

Создать тестовое приложение:

class AndroidTestApp : DaggerApplication() {

    override fun onCreate() {
        super.onCreate()

        Timber.plant(Timber.DebugTree())
    }

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
            DaggerAndroidTestAppComponent.builder().app(this).build()
}

затем бегун:

class AndroidTestAppJunitRunner : AndroidJUnitRunner() {

    override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
        return super.newApplication(cl, AndroidTestApp::class.java.canonicalName, context)
    }
}

включить в android закрытие в Gradle:

testInstrumentationRunner "com.package.name.AndroidTestAppJunitRunner"

добавьте эти зависимости:

kaptAndroidTest "com.google.dagger:dagger-compiler:$daggerVersion"
kaptAndroidTest "com.google.dagger:dagger-android-processor:$daggerVersion"

androidTestImplementation "org.mockito:mockito-android:2.27.0"
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

затем тест:

@RunWith(AndroidJUnit4::class) class LoginActivityTest {

    @Rule
    @JvmField
    val activityRule = ActivityTestRule<LoginActivity>(LoginActivity::class.java, false, false)

    @Before
    fun beforeEach() {
Mockito.doReturn(NOT_SIGNED).`when`(AndroidTestModule.mock).getUserSignedStatus(ArgumentMatchers.anyBoolean()
    }

    @Test
    fun buttonLogin() {
        activityRule.launchActivity(Intent())
        onView(withText("Google")).check(matches(isDisplayed()));
    }
}

Затем ваша зависимость будет предоставлена ​​через сгенерированный график тестовых компонентов в LoginActivity

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...