匕首柄测试EntryPointAccessors



嗨,我有一个类,它实现了一个抽象,参数类型为Foo,并通过EntryPointAccessors直接提供给抽象类。像这样

SomethingImportantBuilderImpl(
...
): AbstractSomethingImportantBuilder(
foo: Foo = EntryPointAccessors.fromApplication(applicationContext, FooEntryPoint::class.java).provideFoo()
)

一切都按预期工作,但现在我如何通过EntryPointAccessor提供Foo在单元测试中的SomethingImportantBuilder ?

每次运行test时,它都会说下一个:

Could not find an Application in the given context: null
java.lang.IllegalStateException: Could not find an Application in the given context: null
at dagger.hilt.android.internal.Contexts.getApplication(Contexts.java:42)
at dagger.hilt.android.EntryPointAccessors.fromApplication(EntryPointAccessors.java:37)

任何帮助/建议吗?我如何在单元测试中提供这种依赖?

编辑1:如果我试着像这样嘲笑它:

every { EntryPointAccessors.fromApplication(any(), FooEntryPoint::class.java).provideFoo() } returns mockk(relaxed = true)

抛出第二个错误:android.content.Context.getApplicationContext()Landroid/content/Context;

编辑2:试图使用mock,所以我模拟了FooEntryPoint接口,其中hilt在Singleton/Context接口中实现了这个入口点。. 不工作,因为它没有初始化上下文:

private lateinit var context: Application
@Before
fun setup(){
context = mockk(moreInterfaces = arrayOf(FooEntryPoint::class, GeneratedComponent::class), relaxed = true)
}
@Test
fun useContext(){
println(context) //using context throws the error saying that lateinit var is not initialized.
}

lateinit property context has not been initialized kotlin.UninitializedPropertyAccessException: lateinit property context has not been initialized错误

还是同样的问题。我如何为EntryPoint提供applicationContext ?

你面临着两个不同的问题:

  1. 在UnitTest中使用刀柄
  2. 需要在UnitTest中注入applicationContext

ad 1)我90%肯定你不能在UnitTests中使用Hilt,但如果有人提供不同的信息,我会很高兴地更新这个答案。为什么在UnitTests中不能使用Hilt ?原因与第二条相同(见下文),因为Hilt与AndroidFramework紧密耦合。

ad 2)这是绝对不可能的,这实际上是Android上UnitTests和instrumentation测试之间的主要区别:后者是在Android环境中运行的(真实设备,模拟器等)。正因为如此,你可以测试使用Android类的代码组件。最著名的是ApplicationContext,Context,ActivityFragment。另一方面,unittest是原始的逻辑测试,与任何Android框架组件无关。UnitTest不需要Android类。没有Android类可以用于unittest。

在你的例子中,你想要在一个需要注入ApplicationContext的测试中使用Hilt。这两个都是强有力的指示,你必须在仪器测试中这样做。这里有一个分步说明如何为本视频中描述的仪表测试设置刀柄:

https://www.youtube.com/watch?v=nOp_CEP_EjM

  1. 添加如下gradle dependencies:
androidTestImplementation "com.google.dagger:hilt-android-testing:2.38.1" 
kaptAndroidTest "com.google.dagger:hilt-android-comiler:2.38.1"
  1. 要使用手柄,您需要用一个小型自定义Runner替换AndroidJUnitRunner。为此,创建HiltTestRunner:
import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner
import dagger.hilt.android.testing.HiltTestApplication
class HiltTestRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
}
}
  1. 要告诉gradle关于新的测试运行器,打开/app/build.gradle并交换以下行:

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

使用您刚刚创建的HiltTestRunner的类引用:

testInstrumentationRunner "your.package.HiltTestRunner"

基本上就是这样。现在你可以在你的测试文件夹中创建一个新的Module,就像你在普通的app文件夹中所做的那样。例如:

@Module
@InstallIn(SingletonComponent::class)
class TestDatabaseModule {

@Provides
@Named("test_database")
fun provideDatabase(@ApplicationContext applicationContext: Context): MyDatabase {
return Room.inMemoryDatabaseBuilder(applicationContext, MyDatabase::class.java)
.allowMainThreadQueries()
.build()
}
}
要在TestCode中注入这个数据库,您需要创建一个HiltAndroidRule,并在@Before方法中调用hiltRule.inject():
@HiltAndroidTest
class UserDaoTest {
@get:Rule
var hiltRule = HiltAndroidRule(this)
@get:Rule
val testCoroutineRule = TestCoroutineRule()
@Inject
lateinit var database: MyDatabase
private lateinit var sut: UserDao
@Before
fun createDb() {
hiltRule.inject()
sut = database.userDao()
}
@After
@Throws(IOException::class)
fun closeDb() {
database.clearAllTables()
database.close()
}
@Test
@Throws(Exception::class)
fun insertAndGetUserFromDb_success() = testCoroutineRule.runBlockingTest {
// Given a database with 1 user
val newUser = UserFakes.get()
sut.insert(newUser)
// When fetching the user
sut.get().test {
// Then the user fields should match 
val user = awaitItem()
assert(user.id == newUser.id)
assert(user.name == newUser.name)
}
}
}

附加说明:我正在使用Turbine来简化测试流程。这是上面代码中的一部分,我在流上调用.test {}awaitItem()来等待流的结果。参见涡轮机自述。

相关内容

  • 没有找到相关文章

最新更新