devoured elysium
devoured elysium

Reputation: 105217

Making Quartz jobs unit-testable

I have some old code that is supposed to allow us to first schedule Quartz tasks (that get database persisted) and later on get executed by Quartz. The problem is that the job code looks something like:

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

I was trying to refactor our code to be more inversion-of-control friendly, so I would like to make ServiceA and ServiceB non-singletons. But I'm failing to see how.. a Quartz Job needs to have a 0-args constructor, so I can't pass my dependencies that way (which would have been ideal).

Any workaround that I don't know of? I would like to avoid IoC frameworks, I'm quite content with passing my parameters as implicits in Scala.

Thanks

Upvotes: 0

Views: 111

Answers (2)

Dima
Dima

Reputation: 40510

Sure, you can pass them via constructor. Just have two constructors:

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

Upvotes: 0

Ivan Kurchenko
Ivan Kurchenko

Reputation: 4063

In case if you can't pass service dependencies via constructor, probably extracting base implementation without dependencies might help - but this looks a bit boiler-plate:

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

Hope this helps!

Upvotes: 1

Related Questions