Jerry Preissler
06/01/2023, 7:54 PMclass DokumentMemoryPersistenceAdapter
(entries: List<Dokument>) : LoadDokumentPort, SaveDokumentPort
and the module
val persistenceKoinTestModule = module(createdAtStart = true) {
single {
(entries: List<Dokument>) ->
DokumentMemoryPersistenceAdapter(entries = get { parametersOf(entries) })
} withOptions {
bind<LoadDokumentPort>()
bind<SaveDokumentPort>()
}
}
In my test I do the following
@BeforeEach
fun beforeEach() {
println("BeforeEach")
val entries = emptyList<Dokument>()
val persistenceAdapter: DokumentMemoryPersistenceAdapter by inject { parametersOf(entries) }
}
@Test
fun loadDokumentOkTest() {
val loadDokumentPort by inject<LoadDokumentPort>()
but as a result I get a message "could not create instance for DokumentMemoryPersistenceAdapter" with a root cause
Caused by: org.koin.core.error.NoParameterFoundException: Can't get injected parameter #0 from DefinitionParameters[] for type 'java.util.List'
at app//org.koin.core.parameter.ParametersHolder.elementAt(ParametersHolder.kt:41)
at app//org.codeshards.aktenordner.adapter.outgoing.persistence.PersistenceKoinTestModuleKt$persistenceKoinTestModule$1$1.invoke(PersistenceKoinTestModule.kt:22)
at app//org.codeshards.aktenordner.adapter.outgoing.persistence.PersistenceKoinTestModuleKt$persistenceKoinTestModule$1$1.invoke(PersistenceKoinTestModule.kt:14)
at app//org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:51)
... 81 more
Lines 14 - 22 of my module happen to be the singleton declaration.
I also tried to supply the parameter for the injected interfaces along the following sample
val saveDokumentPort: SaveDokumentPort by inject { parametersOf(entries) }
and got the same results.
A similar test without a bind option works
class ListPresenter(val a: List<Dokument>)
val presenterModule = module {
single {
(a: List<Dokument>) -> ListPresenter(a = get { parametersOf(a) })
}
}
...
@Test
fun listTest() {
val a = emptyList<Dokument>()
val listPresenter: ListPresenter by inject { parametersOf(a) }
so I guess the bind option is the problem.
Any feedback would be appreciated.Łukasz Nowakowski
06/02/2023, 10:19 AMsingle<LoadDokumentPort> { (entries: List<Dokument>) ->
DokumentMemoryPersistenceAdapter(entries = get { parametersOf(entries) })
}
single<SaveDokumentPort> { (entries: List<Dokument>) ->
DokumentMemoryPersistenceAdapter(entries = get { parametersOf(entries) })
}
and then
val port1: LoadDokumentPort by inject { parametersOf(c1) }
val port2: SaveDokumentPort by inject { parametersOf(c2) }
Jerry Preissler
06/02/2023, 10:44 AMŁukasz Nowakowski
06/02/2023, 11:07 AMinterface LoadDokumentPortUseCase {
fun load(): File
}
interface SaveDokumentPortUseCase {
fun save(file: File) //with result or not?
fun save(json: Bleble)
}
class LoadDokumentPortImpl(
db: MySuperDatabase // or ktor:KtorClient or other
) : LoadDokumentPortUseCase {
override fun load(): File { // File or String or sth else
///db.load...
}
}
class SaveDokumentPortImpl(
db: MySuperDatabase // or ktor:KtorClient or other
) : SaveDokumentPortUseCase {
override fun save(file: File) {
}
override fun save(json: Bleble) {
}
}
class DokumentMemoryPersistenceAdapterImpl(
load: LoadDokumentPortUseCase,
save: SaveDokumentPortUseCase
) : DokumentMemoryPersistenceAdapter {
override fun load(entries: List<String>) {
load.load...
}
override fun save(entries: List<String>) {
save.save...
}
}
interface DokumentMemoryPersistenceAdapter {
fun load(entries: List<String>)
fun save(entries: List<String>)
}
val modules = module {
single<KtorClient> {KtorClientImpl(...)}
single<MySuperDatabase> { MySuperDatabaseImpl(...) }
factory<LoadDokumentPortUseCase> { LoadDokumentPortUseCaseImpl(get()) }
factory<SaveDokumentPortUseCase> { SaveDokumentPortImpl(get()) }
single<DokumentMemoryPersistenceAdapter> {DokumentMemoryPersistenceAdapterImpl(get(),get())}
}
val persistenceAdapter: DokumentMemoryPersistenceAdapter by inject
//depends on your architecture on view model or sth else
persistenceAdapter.load
persistenceAdapter.save
Łukasz Nowakowski
06/02/2023, 11:13 AMJerry Preissler
06/03/2023, 10:09 AMval presentId = AggregateId.fromString("e58ed763-928c-4155-bee9-fdbaaadc15f3")
val entry = Dokument(presentId)
val persistenceKoinTestModule = module {
single<DummyDokumentPersistenceAdapter> { DummyDokumentPersistenceAdapter(listOf<Dokument>(entry)) } withOptions {
bind<LoadDokumentPort>()
}
}
and the test case:
class DummyDokumentPersistenceAdapter(): LoadDokumentPort {
private val dokumente: MutableMap<AggregateId, Dokument> = mutableMapOf()
constructor(entries: List<Dokument>): this() {
....
}
....
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class KoinExploration : KoinTest {
@JvmField
@RegisterExtension
@Suppress("unused")
val koinTestExtension = KoinTestExtension.create {
modules(
persistenceKoinTestModule
)
}
val presentId = org.codeshards.aktenordner.adapter.outgoing.persistence.presentId
@Test
fun listTest() {
val adapter: LoadDokumentPort by inject()
when (val result = adapter.loadDokument(presentId)) {
is OperationResult.SUCCESS ->
assertEquals(entry.id, result.value.id)
is OperationResult.ERROR ->
fail(result.message, result.cause)
}
}
}
This does what I need for now. I'll revisit setting the parameters directly from the test code once I have a better understanding of the Koin DSL. Thanks a lot for your help.