The Problem

Let us assume you have a class FooService, which has a method, quite unimaginatively called getBar that takes a function (Int, Int) => Int and a variable number of additional arguments.

class FooService {
    def getBar(baz: (Int, Int) => Int, args: Any*): Int = {
        //...does stuff with baz and args
        bar
    }
}

Let us also assume that we have a class Foo that calls FooService in one of its methods that we needed to test:

class Foo(fooService: FooService) {
    def doImportantThings(arg1: Int, arg2: Int) {
       ...
       vall bar = fooService.getBar(add, arg1, arg2)
       ....
    }

    private def add(x: Int, y: Int): Int = {
        return x + y;
    }
}

The problem is:
* We want to write a unit test of Foo that mocks FooService * We want to verify that FooService.getBar is called using the right arguments.

The Solution

I have outlined the solution in this Scalatest Snippet. To match the varags, we need to use a VarArg Matcher

import org.mockito.Mockito._
import org.mockito.Matchers._
import scala.collection.mutable

"The Foo class" should "do the things that the Foo class does" in {
    val mockFooService = mock[FooService]
    when(mockFooService.getBar(any[(Int, Int) => Int].apply,
        any[Array[AnyRef]]).thenReturn(42)

    val underTest = new Foo(mockService)
    underTest.doImportantThings(20,21)

    verify(mockFooService).getBar(any[(Int, Int)=> Int],
        argThat(new VarArgMatcher(Array(20,21))))
}

class VarArgMatcher(expected: Array[Int]
    extends ArgumentMatcher[Array[AnyRef]] {
       override def matches(argument: AnyRef): Boolean = 
        argument.asInstanceOf[mutable.WrappedArray[AnyRef]].forall {
            actual =>
                expected.contains(actual.asInstanceOf[Int])
        }
}

I'm not going into the detail of what this code does, and hope it's self-explanatory.

"/>

Matching Functions as arguments and varags using Mockito and Scala

Having spent all morning fighting through the vagaries of mocking and verifying methods that take functions as arguments and variable arguments using Scala and Mockito, I thought I'd scribble this down (mostly for myself, as I'll probably hit this selfsame problem again at some point in the future).

The Problem

Let us assume you have a class FooService, which has a method, quite unimaginatively called getBar that takes a function (Int, Int) => Int and a variable number of additional arguments.

class FooService {
    def getBar(baz: (Int, Int) => Int, args: Any*): Int = {
        //...does stuff with baz and args
        bar
    }
}

Let us also assume that we have a class Foo that calls FooService in one of its methods that we needed to test:

class Foo(fooService: FooService) {
    def doImportantThings(arg1: Int, arg2: Int) {
       ...
       vall bar = fooService.getBar(add, arg1, arg2)
       ....
    }

    private def add(x: Int, y: Int): Int = {
        return x + y;
    }
}

The problem is:
* We want to write a unit test of Foo that mocks FooService * We want to verify that FooService.getBar is called using the right arguments.

The Solution

I have outlined the solution in this Scalatest Snippet. To match the varags, we need to use a VarArg Matcher

import org.mockito.Mockito._
import org.mockito.Matchers._
import scala.collection.mutable

"The Foo class" should "do the things that the Foo class does" in {
    val mockFooService = mock[FooService]
    when(mockFooService.getBar(any[(Int, Int) => Int].apply,
        any[Array[AnyRef]]).thenReturn(42)

    val underTest = new Foo(mockService)
    underTest.doImportantThings(20,21)

    verify(mockFooService).getBar(any[(Int, Int)=> Int],
        argThat(new VarArgMatcher(Array(20,21))))
}

class VarArgMatcher(expected: Array[Int]
    extends ArgumentMatcher[Array[AnyRef]] {
       override def matches(argument: AnyRef): Boolean = 
        argument.asInstanceOf[mutable.WrappedArray[AnyRef]].forall {
            actual =>
                expected.contains(actual.asInstanceOf[Int])
        }
}

I'm not going into the detail of what this code does, and hope it's self-explanatory.