Marius Stroe
Marius Stroe

Reputation: 297

Mockito for Objects in Scala

I'm using Scala 2.10, specs2 and Mockito. I want to mock scala.io.Source.fromURL(). The issue seems to be fromURL() is a function in io.Source's object.

val m = mock[io.Source]
m.fromURL returns io.Source.fromString("Some random string.")

It's a pretty straightforward mock in an Unit test. Why isn't it working?

Thanks!

Upvotes: 14

Views: 29076

Answers (3)

krismath
krismath

Reputation: 1969

Good news! With the latest 1.16 release of mockito-scala, you can now mock scala objects.

To enable withObjectMocked feature, it is mandatory to create the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line:

mock-maker-inline 

Example:

object FooObject {   
  def simpleMethod: String = "not mocked!"
}

"mock" should {
  "stub an object method" in {
    FooObject.simpleMethod shouldBe "not mocked!"

    withObjectMocked[FooObject.type] {
      FooObject.simpleMethod returns "mocked!"
      //or
      when(FooObject.simpleMethod) thenReturn "mocked!"

      FooObject.simpleMethod shouldBe "mocked!"
    }

    FooObject.simpleMethod shouldBe "not mocked!"
  } 
}

See: https://github.com/mockito/mockito-scala#mocking-scala-object

Upvotes: 9

jk-kim
jk-kim

Reputation: 1186

Years later, above answer doesn't work as others have pointed out.

And you cannot mock the scala.io.Source object.

Can I mock final / private methods or classes? This is not supported, as mocks generated with macros are implemented as subclasses of the type to mock. So private and final methods cannot be overridden. You may want to try using an adapter or façade in your code to make it testable. It is better to test against a trait/interface instead of a concrete implementation. There are libraries that support this kind of mocking, such as PowerMock. Be aware that this kind of mocking involves Bytecode manipulation, which has the risk that your test double diverges from the actual implementation.

So what I did is an work around to abstract out scala.io.Source.fromUrl() as a function argument and pass in mocked function in tests.

  // original func
  def aFuncThatUsesSource() = {
    val source = scala.io.Source("127.0.0.1:8080/...")
    val result = source.mkString
    Try(source.close())
    result
  }

  // test friendly func that accepts `scala.io.Source.fromURL` as arg
  def aTestFriendlyFunc(makeApiCall: String => BufferedSource) = {
    val source = makeApiCall("127.0.0.1:8080/...")
    val result = source.mkString
    Try(source.close())
    result
  }

....

  // test spec
  def testyMcTesterson = () => {
    val makeApiCall = mockFunction[String, BufferedSource]
    makeApiCall.expects("something...")
      .returns( new BufferedSource(new ByteArrayInputStream("returns something".getBytes)) )
    aTestFriendlyFunc(makeApiCall) shouldEqual "returns something"
  }

Upvotes: 1

cmbaxter
cmbaxter

Reputation: 35463

Instead of mocking it, you could try spying it as follows:

val m = spy(io.Source)

Or you could mock it as follows:

val m = mock[io.Source.type]

But then how are you using Source in the class you are testing? If you had an example class like so:

class MyClass{

  def foo = {
    io.Source.doSomething //I know doSomething is not on Source, call not important
  }
}

Then in order to take advantage of mocking/spying, you'd have to structure your class like so:

class MyClass{
  val source = io.Source
  def foo = {
    source.doSomething
  }
}

And then your test would have to look something like this:

val mockSource = mock[io.Source.type]
val toTest = new MyClass{
  override val source = mockSource
}

In the Java world, static methods are the bane of mocking. In the Scala world, calls to objects can also be troublesome to deal with for unit tests. But if you follow the code above, you should be able to properly mock out an object based dependency in your class.

Upvotes: 24

Related Questions