简体   繁体   English

Koin 依赖项覆盖在测试中不起作用

[英]Koin dependency override is not working in testing

I'm new to testing and I adapted Koin as my dependency injection.我是测试新手,我将 Koin 改编为我的依赖注入。 my application is working fine.我的应用程序运行良好。 it's still having a login function.它仍然有登录 function。 Here's my dependency class这是我的依赖 class

Modules.kt模块.kt

val applicationModule = module (override = true) {
    single { NetworkService.getInstance().getService(APIService::class.java) }
    single { PreferenceManager.getDefaultSharedPreferences(androidContext()) }
}

val activityModule = module {

    scope(named<LoginActivity>()) {
        scoped { (activity: LoginActivity) ->
            Navigation
                .findNavController(activity, R.id.hostFragment)
        }
    }

    scope(named<MainNavigationActivity>()) {
        scoped { (activity: MainNavigationActivity) ->
            Navigation
                .findNavController(activity, R.id.hostFragment)
        }
    }
}

val viewModelModule = module {
    viewModel { LoginViewModel(loginRepository = get()) }
}

val repositoryModule = module (override = true) {
    single { LoginRepository() }
}

I'm trying to write a simple unit test for the login function in my LoginRepository .我正在尝试为我的LoginRepository中的登录 function 编写一个简单的单元测试。 I MockWebServer to moke the response and get the result.MockWebServer来模拟响应并获得结果。 here's the code in LoginRepository这是LoginRepository中的代码

LoginRepository登录存储库

class LoginRepository: KoinComponent {

    val network: APIService by inject()

    var loginMutableData = MutableLiveData<SingleLiveEvent<Resource<UserSession>>>()

    fun getLoginStatus(): LiveData<SingleLiveEvent<Resource<UserSession>>> {
        return loginMutableData
    }

    fun generalLogin(email: String, encryptedPassword: String){

        val login = network.login(email, encryptedPassword)

        login.enqueue(object : Callback<LoginResponse> {

            override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {

                if(response.isSuccessful){

                    if(response.body()?.status == 1){
                        val resource = Resource<UserSession>(true,"Success")
                        response.body().let {
                            if(it?.session != null){
                                resource.data = UserSession(it.session.userId!!,it.session.fullName!!)
                            }
                        }

                        loginMutableData.value = SingleLiveEvent(resource)

                    }else{

                        val resource = Resource<UserSession>(false,response.body()?.msg ?: "Login failed. Try again")
                        loginMutableData.value  = SingleLiveEvent(resource)
                    }

                }else{

                    loginMutableData.value = SingleLiveEvent(Resource(false, response.message()))
                }

            }

            override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
                loginMutableData.value = SingleLiveEvent(Resource(false, t.localizedMessage))
            }

        })

    }
}

so I wrote below test class to test this generalLogin(email: String, encryptedPassword: String) function in LoginRepository .所以我在下面写了测试 class 来测试这个 generalLogin generalLogin(email: String, encryptedPassword: String) function in LoginRepository

LoginRepositoryTest登录存储库测试

class LoginRepositoryTest : KoinTest {

    private val loginRepository: LoginRepository by inject()
    private val server by lazy { MockWebServer() }
    private lateinit var network: APIService

    @get:Rule
    val rule = InstantTaskExecutorRule()

    @Before
    fun setUp() {

        MockitoAnnotations.initMocks(this)

        startKoin {
            printLogger()
            modules(repositoryModule)
        }
    }

    @Test
    fun testLoginWithCorrectCredentials() {

        server.enqueue(
            MockResponse()
                .setResponseCode(200)
                .setBody("{\"msg\":\"success\",\"status\":1,\"session\":{\"userName\":\"Chathuran\",\"loggedin_user_email\":\"valid_email_address@gmail.com\"}}")
        )
        server.start()
        val testingUrl = server.url("account/userAuth/api_login/")

        network = NetworkService.getInstance().getService(APIService::class.java, testingUrl)
        loadKoinModules(module{network})

        val email = "valid_email_address@gmail.com"
        val password = "valid_password"

        loginRepository.generalLogin(email, password)

        loginRepository.getLoginStatus().observeForever {

            it.getContentIfNotHandled()?.also { resource ->
                Assert.assertEquals(email, resource.data?.email)
            }
        }
    }

    @After
    fun tearDown() {
        stopKoin()
        server.shutdown()
    }
}

So in this class, I tried to override the APIService instance with the one I created in test calls.所以在这个 class 中,我尝试用我在测试调用中创建的实例覆盖APIService实例。 This way I can tell my Retrofit instance to use the base URL provided by MockWebServer .这样我可以告诉我的Retrofit实例使用 MockWebServer 提供的基础MockWebServer But I'm getting the following error from Koin .但是我从Koin收到以下错误。

org.koin.core.error.NoBeanDefFoundException: No definition found for 'com.findmyfare.mobile.app.network.APIService' has been found. Check your module definitions.

    at org.koin.core.scope.Scope.findDefinition(Scope.kt:170)
    at org.koin.core.scope.Scope.resolveInstance(Scope.kt:164)
    at org.koin.core.scope.Scope.get(Scope.kt:128)
    at com.findmyfare.mobile.app.repository.LoginRepository$$special$$inlined$inject$1.invoke(Scope.kt:327)
    at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
    at com.findmyfare.mobile.app.repository.LoginRepository.getNetwork(LoginRepository.kt)
    at com.findmyfare.mobile.app.repository.LoginRepository.generalLogin(LoginRepository.kt:29)
    at com.findmyfare.mobile.app.repository.LoginRepositoryTest.testLoginWithCorrectCredentials(LoginRepositoryTest.kt:56)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

If this is wrong what is the correct way to override APIService instance?如果这是错误的,那么覆盖APIService实例的正确方法是什么? Thanks.谢谢。

Edit: my build.gradle dependencies编辑:我的 build.gradle 依赖项

// Koin
    def koin_version = '2.0.1'
    implementation "org.koin:koin-androidx-scope:$koin_version"
    implementation "org.koin:koin-androidx-viewmodel:$koin_version"
    implementation "org.koin:koin-androidx-ext:$koin_version"
    testImplementation "org.koin:koin-test:$koin_version"

//Testing
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    testImplementation 'junit:junit:4.12'
    testImplementation "org.mockito:mockito-core:2.21.0"
    testImplementation 'android.arch.core:core-testing:1.1.1'
    testImplementation 'com.squareup.okhttp3:mockwebserver:4.2.1'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

You're actually not overriding a bean with your module你实际上并没有用你的模块覆盖一个bean

network = NetworkService.getInstance().getService(APIService::class.java, testingUrl)
loadKoinModules(module{network})

Instead declare the network bean as an override而是将网络 bean 声明为覆盖

network = NetworkService.getInstance().getService(APIService::class.java, testingUrl)
loadKoinModules(module { single(override=true) { network } })

In addition you don't need to use override with your main modules.此外,您不需要对主模块使用override

With your implementation you don't even need an override with the above.通过您的实现,您甚至不需要使用上述内容进行override You're not starting the main module with Koin before the test.在测试之前,您没有使用Koin启动主模块。 This might result in another issue, so make sure to have all modules you need running.这可能会导致另一个问题,因此请确保您需要运行所有模块。

startKoin {
    printLogger()
    modules(applicationModule, repositoryModule)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM