Monday, May 20, 2013

Providing mocks through data providers

Over the weekend I played a bit with TestNG data providers for the first time. This was in a scala program, but doesn't really have anything to do with scala per se. I had something like this in my test class. This is an example, it may not actually run, but it's representative of what I'd written. Key here is that the mock objects were being put together and set up in the data provider. The mocking framework here is Mockito, but I imagine this type of problem could crop up with other frameworks too.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class CarTest extends ShouldMatchers {

  var trackMock1: Track = _
  var positionMock1: Position = _
  var trackMock2: Track = _
  var positionMock2: Position = _

  @BeforeMethod
  def setupMocks() {
    trackMock1 = mock(classOf[Track], "track1")
    positionMock1 = mock(classOf[Position], "pos1")
    trackMock2 = mock(classOf[Track], "track2")
    positionMock2 = mock(classOf[Position], "pos2")
  }

  @DataProvider(name = "positionsProvider")
  def positionsProvider() = {
    def makeTestPosition(position: Position, track: Track) = {
      when(position.track).thenReturn(track)
      Array[Object](position, track)
    }
    Array(makeTestPosition(positionMock1, trackMock1),
          makeTestPosition(positionMock2, trackMock2))
  }

  @Test(dataProvider = "positionsProvider")
  def carPositionHasRightTrack(position: Position, track: Track) {
    val underTest = Car(position)
    car.track should be (track)
  }
}

This doesn't work, with a failure message saying that Track track2 does not match Track track2. The debugger showed me the objects being compared indeed had different identifiers, which led me to conclude that the test was being run as two independent method executions, while the data provider was executed only once. The mocks changed values on each execution of the test method, while the data provider kept the mock values from the first execution. The fix isn't entirely straightforward, and required a combination of changes:
  1. Create mocks whose methods you want to provide specific behavior within the data provider.
  2. Don't re-create the other mocks. Just reset them.
My tests involved many interacting objects, and several test cases to check those interactions. I didn't like duplicating the mock setup code from scratch for each test. However, reset on all mocks (including the Position mocks) would cause loss of behavior between test cases. So the creation of the Position mocks had to move into the data provider. This version of the test ran correctly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class CarTest extends ShouldMatchers {

  val trackMock1: Track = mock(classOf[Track], "track1")
  val trackMock2: Track = mock(classOf[Track], "track2")

  @BeforeMethod
  def setupMocks() {
    reset(trackMock1)
    reset(trackMock2)
  }

  @DataProvider(name = "positionsProvider")
  def positionsProvider() = {
    def makeTestPosition(track: Track) = {
      val positionMock = mock(classOf[Position])
      when(positionMock.track).thenReturn(track)
      Array[Object](positionMock, track)
    }
    Array(makeTestPosition(trackMock1),
          makeTestPosition(trackMock2))
  }

  @Test(dataProvider = "positionsProvider")
  def carPositionHasRightTrack(position: Position, track: Track) {
    val underTest = Car(position)
    car.track should be (track)
  }
}

Interestingly, Mockito developers seem to have been very concerned about the potential for reset to lead to poor coding practices, and had to be convinced that there were legitimate uses for it. While my code doesn't fall into the situations they had foreseen, calls to reset are confined to the test setup.

Wednesday, May 15, 2013

REST-Over-Finagle

I spent a little time searching for REST implementations that work with Finagle. Here's a short list of approaches and implementations. Sources include google and this post, and this post:

I'd love to get more recommendations.

Thursday, May 2, 2013

Scala: Specializing Maps

Scala has put a lot of work into implementing a nice type system, and a usable language on top of that type system. However, sometimes I find myself trying to do things that were easy in Java, and failing. For instance, creating a subclass of a map where the type arguments have been fixed, while retaining the nice factory methods etc. that Scala provides for the built-in Map. For the purpose of this post, let's say I want to implement a specialized IntToStrings map.

A few initial searches and experimentation led me to conclude that you don't want to do something like this:

class IntToStrings extends Map[Int, String] ...

This turns out to be quite a bit of work. And you don't get to use any of the Map factory methods. Instead, you're better off using the MapProxy as a starting point:

class IntToStrings extends MapProxy[Int, String] ...

In this scheme, we construct objects of type Map[Int, String], and wrap them in an IntToStrings. We have a choice to do this wrapping early or late. Late wrapping implies we wait until we need to perform an operation specific to IntToString. Early wrapping by contrast implies we do this operation well in advance, so that perhaps we can pass an appropriately typed object around our program. I tend to think early wrapping would be preferable, as we'd be taking better advantage of type information.

The transcript below executed on the scala REPL should demonstrate this idea more completely. We start with a raw map, and define the class IntToStrings. We then explore the conditions where operations defined on the proxy can be applied to the simple map.

scala> val m: Map[Int, String] = Map(1 -> "1", 2 -> "2")
m: Map[Int,String] = Map(1 -> 1, 2 -> 2)

scala> :paste
// Entering paste mode (ctrl-D to finish)

class IntToStrings(val self: Map[Int, String]) extends scala.collection.MapProxy[Int, String] {
  def getSum = self.foldLeft((0, "")) {
    (sum, entry) => (sum._1 + entry._1, sum._2 + entry._2)
  }
}

object IntToStrings {
  implicit def rawToIntToStrings(map: Map[Int, String]) = new IntToStrings(map)
  implicit def intStringMapToRaw(map: IntToStrings) = map.self
}

// Exiting paste mode, now interpreting.

defined class IntToStrings
defined module IntToStrings

Key to making IntToStrings usable is to ensure we have simple mechanisms for converting Map[Int,String] to it. The scala compiler needs to know that a conversion is applicable and desired. It isn't possible to have the compiler guess that we want to convert the map to an IntToStrings just by attempting the execution of an operation that the type provides. The compiler doesn't have sufficient information.

scala> m.getSum
<console>:9: error: value getSum is not a member of Map[Int,String]
              m.getSum

There are two ways of telling the compiler you want a conversion. The first is to make clear that we want an IntToStrings as the result of some computation, as below. In this case the compiler knows the type we want, looks if the companion object provides any implicit conversion methods, and applies it.

scala> val rm: IntToStrings = m
rm: IntToStrings = Map(1 -> 1, 2 -> 2)

scala> rm.getSum
res17: (Int, java.lang.String) = (3,12)

The second approach is to import the converters, bring them in scope, after which the compiler is able to do the implicit conversion to execute getSum.

scala> import IntToStrings._
import IntToStrings._

scala> m.getSum
res19: (Int, java.lang.String) = (3,12)

Rules for implicit conversion are well documented at many places. They're worth repeating here, as they are so critical to having proxy classes do what we would like: recognize that we have a map on which we wish to execute specialized operations. Which of these is preferable depends a great deal on how you wish to structure the program: do you want to pass around a data structure with the specific name you've given to the proxy class, or would you rather just operate on the map and execute operations as needed?

There is one other consideration here. It probably isn't a good idea to have the proxy class store additional state. It can be easily lost as you convert back and forth to the underlying map. In any case, adding state to the proxy is a bit of an abuse of the class, as we don't really have a pure proxy any more. You'll probably want to come up with a different way to deal with the object in that case.