1

I have a similar problem as mentioned in link: How to mock a function within Scala object using Mockito?

I have an object FooInner with method stringData, which takes int and give string output

object FooInner {
  def stringData(data: Int): String = {
    data match {
      case 1 => "one"
      case _ => "else"
    }
  }
}

Another object, FooOuter, calls FooInner to get string data and perform some operation on it.

object FooOuter {
  def dummyCall(data: Int): String = {
    FooInner.stringData(data) + "some operation"
  }
}

My aim is to test method FooOuter.dummyCall for string data, NOT returned by FooInner.stringData

For same, I followed above mentioned post and created trait

trait TFooInner {
  def stringData(data: Int): String
}

Changed signature of FooInner to

object FooInner extends TFooInner {..}

And created test class FooTests

class FooTests extends FlatSpec with Matchers with MockitoSugar{
  "dummyCall" should "mocked data" in {
    val service = mock[TFooInner]
    when(service.stringData(1)).thenReturn("1")    // mocked service data -> 1
    assert(FooOuter.dummyCall(1) === "1-some operation")   // TestFailedException:  "[one]-some operation" did not equal "[1]-some operation"
  }
}

, but I am still not able to get mocked service data "1". I have following questions:

  • How can I make FooOuter testable with data, not returned from FooInner
  • Is it right functional style to code in Scala? What I feel is FooOuter is now tightly-coupled/dependent on FooInner

Scala: 2.11
Mockito: 1.9.5

user811602
  • 1,314
  • 2
  • 17
  • 47
  • From what I can understand from your question, you're not mocking what your function `stringData` can actually do - when `stringData` receives a 1, it should return "one" not "1". Why do you expect this to work? – James Whiteley Jan 28 '19 at 11:31
  • And I don't understand what you mean by "How can I make FooOuter testable with data, not returned from FooInner" - Mockito is used to mock function calls, and your function calls a function within FooInner. Why do you expect to not mock this function out? – James Whiteley Jan 28 '19 at 11:33
  • @JamesWhiteley, from line val service = mock[TFooInner]; when(service.stringData(1)).thenReturn("1"), I thought, I am mocking FooInner.stringData only. Is this not true?If not, how I can I mock same, so that it can be consume by FooOuter.dummyCall – user811602 Jan 28 '19 at 11:39
  • You are mocking the output of stringData, when it is given the input `1`. Unless I'm misunderstanding, the output of your function `dummyCall` changes from `FooInner.stringData(data) + "some operation"` to `{your mock thenReturn bit} + "some operation"`. If you make the mock return "1", the output of dummyCall will be "1some operation", which is what your error is complaining about. You'd want the mock to return "one", like the actual function does, when p[assed 1 as an input. – James Whiteley Jan 28 '19 at 12:21

1 Answers1

7

You have to alter both Inner and Outer objects to make them testable.

In general, you should never invoke object methods directly, if you might want to stub them for testing. Such methods should be implemented and accessed via instance calls:

  class FooInner {
     def stringData(data: Int): String = { ... }
  }

  object FooInner extends FooInner

   class FooOuter(inner: FooInner) {
     def dummyCall(data: Int): String = inner.stringData(data) + "bar"
   }

   object FooOuter extends FooOuter(FooInner)

Now, in your test, you can do

   val inner = mock[FooInner]
   val testMe = new FooOuter(inner)
   when(inner.stringData(any)).thenReturn("foo")
   testMe.dummyCall(1) shouldBe "foobar"
   verify(inner).stringData(1)
Dima
  • 39,570
  • 6
  • 44
  • 70