Unit testing class that extends a trait - how do I

2019-08-06 05:53发布

问题:

I'm using scalatest to unit test a class that extends a trait (in the sense that my class is using the trait as a mixin).

The trait contains methods that are helper methods (which ultimately call a few lookups in a database) which I would like to stub out so that I can isolate just the functionality of my class. But I havent been able to find a framework like Mockito or ScalaMock that makes that possible.

Question: Can it be achieved with a mocking framework, and if so how? And if not, I'd be interested to know if there's a reason why.

eg.

trait MyTrait {
  def usefulMethod(i: int) = {...}
}


class MyClass extends MyTrait {
  def numberCruncher = {
     val x = usefulMethod(1) + 1
  }
}

I need to return a different stubbed answer depending on the value of i. Something akin to Mockito's when(myTrait.usefulMethod(1)).thenReturn(10)

I also need to verify that a different method is called in the trait with the correct value.

In essence I am asking this question again but I notice this was asked in 2011, and things may well have moved on. There may be new frameworks and new approaches. This 2001 question also doesnt ask about how to verify methods in the traits.

Question: Do the use of traits-as-a-mixin in this way actually prevent the ability to unit test using a mocking framework to mock/stub methods from the mixin? i.e. Do the mocking and stubbing frameworks depend on the use of dependency injection?

PS. I've been using Mockito until now but I'm not averse to using scalamock or any other framework. I've looked into scalamock because it advertises that it can test traits but from scalamock's own tests it appears that while it can mock traits you can only stub mock the behaviour if that trait is passed as a dependency to a class, rather than a class extending that trait. See here

回答1:

I generally subclass the class under test to replace methods I want to mock out:

var usefulInput: Option[Int] = Nont
val testInstance = new MyClass {
  override def usefulMethod(i: Int) = {
    usefulInput = Some(i)
    42
  }
}

testInstance.numberCruncher should equal(43)
usefulInput should equal(Some(1))

I find inline overriding generally simpler to implement and read than any mocking framework