Search code examples
scalamockitotraitsscalatest

Scala mocking trait that is extended by another trait


I am trying to test

trait Name extends Helper {
    def name() = {
      var s = getSystem()
      s.name()
    }
}

where all I want to do is make sure that the "s.name()" method is invoked once by mocking an instance of s, which is a System.

Helper is defined as so:

trait Helper {
    def getSystem() : System = {
       systemGetter.get()
    }
}

As of now, my NameSpec looks something like:

class NameSpec extends FlatSpec with Matchers with MockitoSugar {

    class NameImpl extends Name
    var toTest = new NameImpl

    val mockSystem = mock[System]

    it should "call s.name() once" in {
        when(getSystem() is invoked, return a mockSystem) 
        toTest.name()
        // Verify that mockSystem.name() happened only once
    }
}

What I'm confused about is how to return a mock System in toTest.name() when it calls getSystem() so that I can verify that the system calls s.name() only once. I could easily mock this System if it were a parameter to the name() method in Name trait, so I guess I don't know how to "inject" a mockSystem to be used instead of a real system when that method is invoked.


Solution

  • Unfortunately your code is not compilable and thus is obviously an inadequate representation of what you really have. Particularly it is not clear how the Helper really gets an object of type System. I think that in the real code you should mock the systemGetter, that I suppose is somehow injected into the objects implementing Helper trait, to return your mockSystem. However it is hard to show you a valid example of that basing on the code you provided. If for some reason this is not what you can do, there are a few more avenues.

    You seem to use something like Cake pattern around Helper and its inheritance. If so, you can use a class instead of NameImpl to inject System:

    class NameWithInjectedSystem(val system: System) extends Name {
      override def getSystem(): System = system
    }
    
    it should "call s.name() once" in {
      val mockSystem = mock[System]
      val nameToTest = new NameWithInjectedSystem(mockSystem)
      val mockName = "Mock name"
    
      when(mockSystem.name()).thenReturn(mockName)
      val actual = nameToTest.name()
      actual should === (mockName)
      verify(mockSystem, times(1)).name()
    }
    

    Finally you can mock even nameToTest object itself but this is not the way I'd suggest because it binds test to much more implementation details than you should want:

    it should "call s.name() once" in {
      val mockSystem = mock[System]
      val nameToTest = mock[NameImpl]
      when(nameToTest.getSystem()).thenReturn(mockSystem)
      when(nameToTest.name()).thenCallRealMethod()
      val mockName = "Mock name"
      when(mockSystem.name()).thenReturn(mockName)
      val actual = nameToTest.name()
      actual should ===(mockName)
      verify(mockSystem, times(1)).name()
    }
    

    Note how you must call thenCallRealMethod for the .name() call and so you should do for all the calls inside the name or the test will not work.