【问题标题】:NoBeanDefFoundException with Mock ViewModel, testing with Koin, EspressoNoBeanDefFoundException 与 Mock ViewModel,使用 Koin、Espresso 进行测试
【发布时间】:2019-11-06 10:30:46
【问题描述】:

我一直在尝试使用Koin 作为 DI 工具进行简单的Espresso 单元测试工作。这是我在build.gradle中使用的依赖项

    // testing with Koin
    // because of this
    // https://github.com/InsertKoinIO/koin/pull/604/commits/69391bc378bbb9007b9d82c46537e7d753be7ea3
    androidTestImplementation 'org.mockito:mockito-android:3.1.0'
    androidTestImplementation ("org.koin:koin-test:$koin_version") {
        exclude group: 'org.mockito'
    }

    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    // stuff like ActivityTestRule
    androidTestImplementation 'androidx.test:rules:1.2.0'
    // AndroidJUnit4
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    // test runner
    androidTestImplementation 'androidx.test:runner:1.2.0'

我的ViewModel 声明

open class LoginViewModel(private val apiService: MockApiService) : ViewModel() {
..
..
}

这是它在 Activity 中的注入方式

private val loginViewModel: LoginViewModel by viewModel()

我的自定义 TestRunner 以便实例化自定义 TestApplication

class MyTestRunner : AndroidJUnitRunner() {
    override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
        return super.newApplication(cl, TestApplication::class.java.name, context)
    }
}

TestApplication 类。我已经验证了这个测试类在调用测试时被初始化

class TestApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger()
            androidContext(this@TestApplication)
            modules(emptyList())
        }
    }
}

这是我的实际androidTest。一旦使用NoBeanDefFoundException 启动活动,此操作就会失败

未找到“com.abhishek.mvvmdemo.onboarding.LoginViewModel”的定义。

@RunWith(AndroidJUnit4::class)
@LargeTest
class LoginActivityTest : KoinTest {
    private lateinit var loginViewModel: LoginViewModel

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

    @Before
    fun beforeTest() {
        loginViewModel = declareMock()
        loadKoinModules(
            module {
//                single { ApiModule.providesApiService() }
                viewModel { loginViewModel }
            }
        )
    }

    @Test
    fun testProgress() {
        activityRule.launchActivity(null)
        onView(withId(R.id.emailEt))
            .perform(ViewActions.typeText("abhishek"))
    }

    @After
    fun afterTest() {
        stopKoin()
    }
}

我尝试了很多排列和组合,但都没有运气。我的 gradle 中也碰巧有以下配置


testOptions {
        animationsDisabled = true
    }

    packagingOptions {
        pickFirst 'mockito-extensions/org.mockito.plugins.MockMaker'
    }

testInstrumentationRunner "com.abhishek.mvvmdemo.MyTestRunner"

TL;DR

这里是重现问题的github sample

【问题讨论】:

    标签: android android-espresso android-testing androidx koin


    【解决方案1】:

    这里发生的情况是 ActivityTestRule 在您的 @Before 方法之前启动活动,因此模拟没有机会被初始化。

    来自官方documentation

    此规则提供单个 Activity 的功能测试。什么时候 launchActivity在构造函数中设置为true,Activity下 test 将在使用 Test 注释的每个测试之前和之前启动 带有 Before 注解的方法,在 测试完成,After注解的方法也完成了。

    您应该使用constructor 指定您不想自动启动活动

    ActivityTestRule (Class<T> activityClass, 
                    boolean initialTouchMode, 
                    boolean launchActivity)
    

    然后在您的测试方法中,您可以手动启动您的活动

    activityRule.launchActivity(null)
    

    此外,您可能需要查看 https://mockk.io/ 进行模拟。你不必将你的类声明为open

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多