Say I have a simple Fantom app to retrieve data from a MongoDB database. The app uses afIoc (AlienFactory's IoC framework) and afMorphia (AlienFactory's MongoDB framework). I have a simple Entity called Foo and a DAO class (FooDao) to retrieve Foo entities from the database. I want to test the DAO class using fant command but I'm getting the error below. What am I doing wrong or is there a better way to write the test?. Please note that this app doesn't use afBedSheet web framework.
TEST FAILED
afIoc::IocErr: No dependency matches type sys::Type.
Ioc Operation Trace:
[ 1] Injecting dependencies into fields of testAfIoc::FooDaoTest
[ 2] Injecting dependencies into fields of testAfIoc::FooDaoTest
[ 3] Looking for dependency of type testAfIoc::FooDao?
[ 4] Creating REAL Service 'testAfIoc::FooDao'
[ 5] Creating 'testAfIoc::FooDao' via ctor autobuild
[ 6] Injecting dependencies into fields of testAfIoc::FooDao
[ 7] Looking for dependency of type afMorphia::Datastore?
[ 8] Creating REAL Service 'afMorphia::Datastore'
[ 9] Creating 'afMorphia::Datastore' via ctor autobuild
[10] Determining injection parameters for afMorphia::DatastoreImpl sys::Void make(sys::Type type, afMongo::Database database, |sys::This->sys::Void| in)
Stack Trace:
afIoc::Utils.stackTraceFilter (Utils.fan:50)
afIoc::RegistryImpl.injectIntoFields (RegistryImpl.fan:253)
testAfIoc::FooDaoTest.setup (FooDaoTest.fan:17)
java.lang.reflect.Method.invoke (Method.java:483)
fan.sys.Method.invoke (Method.java:559)
fan.sys.Method$MethodFunc.callList (Method.java:204)
fan.sys.Method.callList (Method.java:138)
fanx.tools.Fant.runTest (Fant.java:190)
fanx.tools.Fant.test (Fant.java:110)
fanx.tools.Fant.test (Fant.java:32)
fanx.tools.Fant.run (Fant.java:284)
fanx.tools.Fant.main (Fant.java:327)
Time: 377ms
Failed:
testAfIoc::FooDaoTest.testFindAll
***
*** 1 FAILURES [1 tests, 0 methods, 0 verifies]
***
This is how my classes look like (explicit "using" statements to show what classes come from what Fantom pods):
fan/AppModule.fan
using afIoc::Configuration
using afIoc::Contribute
using afIoc::ServiceDefinitions
using afIocConfig::ApplicationDefaults
using afMorphia::MorphiaConfigIds
using afMorphia::Datastore
class AppModule {
@Contribute { serviceType=ApplicationDefaults# }
static Void contributeAppDefaults(Configuration config) {
config[MorphiaConfigIds.mongoUrl] = `mongodb://localhost:27017/foo`
}
static Void defineServices(ServiceDefinitions defs) {
defs.add(FooDao#)
defs.add(Datastore#)
}
}
fan/Foo.fan
using afMorphia::Entity
using afMorphia::Property
using afBson::ObjectId
@Serializable
@Entity { name="foo" }
class Foo {
@Property const ObjectId _id
@Property const Str text
new make(|This| f) { f(this) }
}
fan/FooDao.fan
using afIoc::Inject
using afMorphia::Datastore
class FooDao {
@Inject { type=Foo# }
Datastore? datastore
Foo[] all() {
(Foo[]) datastore.findAll
}
}
fan/FooDaoTest.fan
using afIoc::Inject
using afIoc::RegistryBuilder
using afIocConfig::ConfigModule
using afMorphia::Datastore
class FooDaoTest : Test {
@Inject
FooDao? subject
override Void setup() {
registry := RegistryBuilder().addModules([
AppModule#
,ConfigModule#
,Datastore#
]).build.startup
registry.injectIntoFields(this)
}
Void testFindAll() {
verifyEq ( subject.all.size, 2 )
}
}
build.fan
#! /usr/bin/env fan
using build
class Build : BuildPod {
new make() {
podName = "testAfIoc"
summary = "Testing AF IoC"
version = Version("0.0.1")
depends = [
"sys 1.0+",
"afIoc 2.0.2+",
"afIocConfig 1.0.16+",
"util 1.0+",
"afMongo 1.0.0+",
"afBson 1.0.0+",
"concurrent 1.0.8+",
"afMorphia 1.0.2+"
]
srcDirs = [
`fan/`,
]
meta = [
"proj.name" : "Testing Alien Factory IoC framework",
"afIoc.module" : "testAfIoc::AppModule",
"org.name" : "Foo",
"org.uri" : "http://www.example.com/"
]
}
}
Everything is setup right, and the way you're testing the DAO is spot on. (Morphia doesn't need Foo
to be @Serializable
by the way...)
The problem is where you build the Registry
. Because Morphia is an IoC library, you need to add Morphia's module to the registry. Also note that Datastore
is not a module and should not be added:
In fan/FooDaoTest.fan
using afIoc::Inject
using afIoc::RegistryBuilder
using afIocConfig::ConfigModule
using afMorphia::MorphiaModule
class FooDaoTest : Test {
@Inject
FooDao? subject
override Void setup() {
registry := RegistryBuilder()
.addModule(AppModule#)
.addModulesFromPod("afMorphia")
.addModulesFromPod("afIocConfig")
.build.startup
registry.injectIntoFields(this)
}
Void testFindAll() {
verifyEq ( subject.all.size, 2 )
}
}
When building your own Registry
, make sure all the relevant IoC modules and libraries are added.
I'm surprised that the code didn't throw a 'Datastore' Service Not Found Err
(which is more telling) - I'll look in to that...
Ah, yes. In your AppModule
there's no need to add Datastore
as a service. All libraries should define their own services in their module, so all you should ever need to do is add that module (and maybe configure some services).
(Morphia has a special dependency provider for building Datastore
instances which is why it didn't work when you added it as a plain service.)
Removing the Datastore
definition from AppModule
and re-running the broken test gives:
TEST FAILED
afIoc::IocErr: No service matches type afMorphia::Datastore?.
which is (a bit) more informative and what I would expect.