0

I want to write unit test for below scala class. In the below implementation, QueryConfig is final case class.

class RampGenerator {
  def createProfile(queryConfig: QueryConfig): String = {
    new BaseQuery(queryConfig).pushToService().getId
  }
}

The unit test I have written is this

@RunWith(classOf[JUnitRunner])
class RampGeneratorTest extends FlatSpec with Matchers {
  "createProfile" must "succeed" in {
    val rampGenerator = new RampGenerator()

    val queryConfig = QueryConfig("name", "account", “role")
    val baseQuery = mock(classOf[BaseQuery])
    val profile = mock(classOf[Profile])

    when(new BaseQuery(queryConfig)).thenReturn(baseQuery)
    when(baseQuery.pushToService()).thenReturn(profile)
    when(profile.getId).thenReturn("1234")
    val id = rampGenerator.createProfile(queryConfig)
    assert(id.equals("1234"))
  }
}

Currently it gives below exception, which is expected, since I don't have mocked class used in when. How do I mock the new instance creation?

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);
BigDataLearner
  • 1,388
  • 4
  • 19
  • 40

1 Answers1

1

There are two options:

  1. Use powermockito to mock the constructor (see this question for details)
  2. Externalize object creation

A bit more on the second option - this is actually a testing technique that helps in a variety of situations (a couple of examples: yours, creating akka actors and asserting on hierarchies) - so it might be useful to just have it in the "toolbox".

In your case it'll look something like this:

class RampGenerator(queryFactory: QueryFactory) {
   def createProfile(queryConfig: QueryConfig) = queryFactory.buildQuery(queryConfig).pushToService().getId()
}

class QueryFactory() {
   def buildQuery(queryConfig: QueryConfig): BaseQuery = ...
}


@RunWith(classOf[JUnitRunner])
class RampGeneratorTest extends FlatSpec with Matchers {
  "createProfile" must "succeed" in {
    val rampGenerator = new RampGenerator()

    val queryConfig = QueryConfig("name", "account", “role")
    val queryFactory = mock(classOf[QueryFactory])
    val profile = mock(classOf[Profile])
    val baseQuery = mock(classOf[BaseQuery])

    when(queryFactory.buildQuery(queryConfig)).thenReturn(baseQuery)
    when(baseQuery.pushToService()).thenReturn(profile)
    when(profile.getId).thenReturn("1234")
    val id = rampGenerator.createProfile(queryConfig)
    assert(id.equals("1234"))
  }
}

Please note query factory does not have to be a separate factory class/hierarchy of classes (and certainly does not require something as heavyweight as abstract factory pattern - although you can use it). In particular, my initial version was just using queryFactory: QueryConfig => BaseQuery function, but mockito cannot mock functions...

If you prefer to inject factory method directly (via function), Scalamock has support for mocking functions

J0HN
  • 26,063
  • 5
  • 54
  • 85
  • mockito-scala can mock functions ;) – ultrasecr.eth Aug 19 '19 at 09:10
  • @Bruno Good point, thanks! In my defense, OP didn't list mockito-scala is used, and I haven't used (or came accross it) as well. Anyway, I think I still like scalatest better, because mockito's way of setting up multiple responses on the same mock (including [mockito-scala](https://github.com/mockito/mockito-scala#function-answers)) is much les sintuitive and more verbose than [scalamock](https://scalamock.org/user-guide/mocking_style/#example-using-record-then-verify-style-only) – J0HN Aug 19 '19 at 09:18
  • I just looked at the scalamock example and it seems more verbose and less intuitive, I don't know if comparing answer stubbing is correct with the scalamock examples you posted, if you like ping me in the gitter channel of mockito-scala and we can discuss it better :) – ultrasecr.eth Aug 19 '19 at 10:58