【问题标题】:Making Quartz jobs unit-testable使 Quartz 作业可单元测试
【发布时间】:2020-02-03 12:36:24
【问题描述】:

我有一些旧代码应该允许我们首先安排 Quartz 任务(使数据库持久化),然后由 Quartz 执行。问题是工作代码看起来像:

class MyJob extends Job {
  def execute(context: JobExecutionContext) {
    ServiceA.m() <-- Singleton call
    ...
    ServiceB.n() <-- Singleton call
  }
}

我试图重构我们的代码,使其对控制反转更友好,所以我想让ServiceAServiceB 成为非单例。但是我看不到.. Quartz Job 需要有一个 0-args 构造函数,所以我不能以这种方式传递我的依赖项(这本来是理想的)。

任何我不知道的解决方法?我想避免使用 IoC 框架,我很满意在 Scala 中将参数作为隐式传递。

谢谢

【问题讨论】:

    标签: java scala quartz


    【解决方案1】:

    如果您不能通过构造函数传递服务依赖项,可能提取没有依赖项的基本实现可能会有所帮助 - 但这看起来有点样板:

      // Base implementation without direct service instantiation 
      trait BaseMyJob extends Job {
        protected val serviceA: ServiceA
        protected val serviceB: ServiceB
    
        def execute(context: JobExecutionContext) {
          serviceA.m()
          ...
          serviceB.n()
        }
      }
    
      // Used in your production code via Quartz 
      class ProductionMyJob extends BaseMyJob {
        override protected val serviceA: ServiceA = ServiceA()
        override protected val serviceB: ServiceB = ServiceB()
      }
    
      // Somewhere in your test spec
      val mockServiceA = mock[ServiceA]
      val mockServiceB = mock[ServiceB]
    
      val testJob: BaseMyJob = class TestMyJob extends BaseMyJob {
        override protected val serviceA: ServiceA = ServiceA()
        override protected val serviceB: ServiceB = ServiceB()
      }
    

    希望这会有所帮助!

    【讨论】:

    • Quartz 将通过反射为您实例化 Jobs(即 0-args 构造函数)。我不明白这如何回答我的问题。
    • @devouredelysium 对不起,我不知道 Quartz 的细节,但正如你提到的,因为它需要 0 参数构造,这导致注入模拟的问题,我认为提取基类使用可以注入它们的方法可能会有所帮助。
    • 这不是因为 Job 实例从不由你创建,它们总是由 Quartz 创建:你调用 JobBuilder.newJob(classOf[myJob]) 来获取实例。
    【解决方案2】:

    当然,您可以通过构造函数传递它们。只要有两个构造函数:

        class MyJob(serviceA: Foo, serviceB: Bar) extends Job {
           def this() = this(ServiceA, ServiceB)
           def execute(context: JobExecutionContext) = {
              serviceA.foo()
              serviceB.bar()
           }
         }
    
         val testMe = new MyJob(mock[Foo], mock[Bar])
    

    【讨论】:

    • ... Quartz 为你实例化 Job 实例,所以这甚至没有意义。
    • @devouredelysium 嗯?什么对你来说“没有意义”?让石英实例化它想要的东西。我认为没有问题。
    • 您避免让我的类在其execute() 方法中依赖单例的计划是在构造函数级别仍然依赖单例吗?我们真的从中获得了什么?
    • @devouredelysium 。对于您的测试,您使用另一个构造函数,并传递要使用的模拟(或任何您想要的)而不是单例,如最后一行所示。 Quartz 仍然使用默认构造函数,它使用单例进行初始化。
    • 我明白了。这是一种解决方法,但仅适用于单元测试(我知道我在 OP 中写了“单元测试”)。我希望有 Quartz 知识的人会知道一个替代 API 来避免这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    • 2012-10-19
    • 1970-01-01
    相关资源
    最近更新 更多