如何对使用 AlienFactory 的 afIoc 和 afMorphia 框架的 Fantom DAO class 进行单元测试?

How to unit-test a Fantom DAO class that uses AlienFactory's afIoc and afMorphia frameworks?

假设我有一个简单的 Fantom 应用程序可以从 MongoDB 数据库中检索数据。该应用程序使用 afIoc(AlienFactory 的 IoC 框架)和 afMorphia(AlienFactory 的 MongoDB 框架)。我有一个名为 Foo 的简单实体和一个 DAO class (FooDao) 来从数据库中检索 Foo 实体。我想使用 fant 命令测试 DAO class,但出现以下错误。我做错了什么或者有更好的方法来编写测试?。请注意,此应用不使用 afBedSheet 网络框架。

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]
***

这就是我的 classes 的样子(显式 "using" 语句显示 classes 来自 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/"
    ]
  }
}

一切都设置正确,您测试 DAO 的方式也恰到好处。 (顺便说一句,Morphia 不需要 Foo 成为 @Serializable...)

问题出在您构建 Registry 的位置。因为Morphia是一个IoC库,所以需要将Morphia的模块添加到注册表中。另请注意,Datastore 不是模块,不应添加:

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 )
  }
}

构建您自己的 Registry 时,确保添加所有相关的 IoC 模块和库。

我很惊讶代码没有抛出 'Datastore' Service Not Found Err(这更能说明问题)- 我会调查一下...

编辑:

啊,是的。在您的 AppModule 中,无需将 Datastore 添加为服务。所有库都应在其模块中定义自己的服务,因此您需要做的就是添加该模块(并可能配置一些服务)。

(Morphia 有一个特殊的依赖提供程序来构建 Datastore 个实例,这就是为什么当您将它添加为普通服务时它不起作用的原因。)

AppModule 中删除 Datastore 定义并重新 运行 broken 测试给出:

TEST FAILED
afIoc::IocErr: No service matches type afMorphia::Datastore?.

这是(有点)更多的信息,也是我所期望的。