com

borachio

package borachio

Borachio: Native Scala mocking

To use Borachio with ScalaTest, mix the com.borachio.scalatest.MockFactory trait into a Suite:

class MyTest extends Suite with MockFactory

To use Borachio with JUnit3, mix the com.borachio.junit3.MockFactory trait into a TestCase:

class MyTest extends TestCase with MockFactory

Borachio supports two different mocking styles - Functional mocking and Proxy mocking.

Functional mocking

Functional mocks are created with mockFunction. The following, for example, creates a mock function taking a single Int argument and returning a String:

val m = mockFunction[Int, String]

Expectations then can be set on a mock function. The following, for example, says that we expect our mock to be called once with the argument 42, and that when called like that it should return the value "Forty two":

m expects (42) returning "Forty two" once

Proxy mocking

Proxy mocks are created with mock. The following, for example, creates a mock which implements all the Turtle trait (interface):

val m = mock[Turtle]

Expectations can then be set on each of the methods within those traits. For example:

m expects 'setPosition withArgs (10.0, 10.0)
m expects 'forward withArgs (5.0)
m expects 'getPosition returning (15.0, 10.0)

By default, an expectation accepts any arguments and a single call. The following two statements are equivalent:

m expects 'forward withArgs (*) once
m expects 'forward

As a convenience, proxy mocks also support the stubs method. The following two statements are equivalent:

m expects 'forward anyNumberOfTimes
m stubs 'forward

Expectations

Expectations can be set on the arguments a function or method is called with and how many times it should be called. In addition, mocks can be instructed to return a particular value or throw an exception when that expectation is met.

Arguments

To specify expected arguments for a functional mock, use expects. To specify expected arguments for a proxy mock, use withArgs or withArguments.

If no expected arguments are given, mocks accept any arguments.

To specify arguments that should simply be tested for equality, provide the expected arguments as a tuple:

m expects ("this", "that")

Borachio currently supports two types of generalized matching: wildcards and epsilon matching.

Wildcards

Wildcard values are specified with an * (asterisk). For example:

m expects ("this", *)

will match any of the following:

m("this", 42)
m("this", 1.0)
m("this", null)
Epsilon matching

Epsilon matching is useful when dealing with floating point values. An epsilon match is specified with the ~ (tilde) operator:

m expects (~42.0)

will match:

m(42.0)
m(42.0001)
m(41.9999)

but will not match:

m(43.0)
m(42.1)
Predicate matching

More complicated argument matching can be implemented by passing a predicate; a function that takes a scala.Product and returns a Boolean. For mock functions, use expectsWhere:

m = mockFunction[Double, Double, Unit]
m expectsWhere { (x: Double, y: Double) => x < y }

For proxy mocks, use where:

m = mock[Turtle]
m expects 'setPosition where { (x: Double, y: Double) => x < y }
Return value

Mocks can be instructed to return a specific value with returns or returning:

m1 returns 42
m2 expects ("this", "that") returning "the other"

If no return value is specified, functional mocks return null.asInstanceOf[R] where R is the return type (which equates to 0 for Int, 0.0 for Double etc.).

If no return value is specified, proxy mocks return null. This works correctly for most return types, but not for methods returning primitive types (Int, Double etc.), where returning null leads to a NullPointerException. So you will need to explicitly specify a return value for such methods. This restriction may be lifted in the future.

You can return a computed value (or throw a computed exception) with onCall, for example:

val mockIncrement = mockFunction[Int, Int]
m expects (*) onCall { x: Int => x + 1 }
Exceptions

Instead of a return value, a mock can be instructed to throw:

m expects ("this", "that") throws new RuntimeException("what's that?")
Call count

By default, mocks expect one or more calls (i.e. only fail if the function or method is never called). An exact number of calls or a range can be set with repeat:

m1 returns 42 repeat 3 to 7
m2 expects (3) repeat 10

There are various aliases for common expectations and styles:

m1 expects ("this", "that") once
m2 returns "foo" noMoreThanTwice
m3 expects (42) repeated 3 times

For a full list, see Expectation.

Ordering

By default, expectations can be satisfied in any order. For example:

m expects (1)
m expects (2)
m(2)
m(1)

A specific sequence can be enforced with inSequence:

inSequence {
  m expects (1)
  m expects (2)
}
m(2) // throws ExpectationException
m(1)

Multiple sequences can be specified. As long as the calls within each sequence happen in the correct order, calls within different sequences can be interleaved. For example:

val m1 = mock[Turtle]
val m2 = mock[Turtle]

inSequence {
  m1 expects 'setPosition withArguments (0.0, 0.0)
  m1 expects 'penDown
  m1 expects 'forward withArguments (10.0)
  m1 expects 'penUp
}
inSequence {
  m2 expects 'setPosition withArguments(1.0, 1.0)
  m2 expects 'turn withArguments (90.0)
  m2 expects 'forward withArguments (1.0)
  m2 expects 'getPosition returning (2.0, 1.0)
}

m2.setPosition(1.0, 1.0)
m1.setPosition(0.0, 0.0)
m1.penDown
m2.turn(90.0)
m1.forward(10.0)
m2.forward(1.0)
m1.penUp
expect((2.0, 1.0)) { m2.getPosition }

To specify that there is no constraint on ordering, use inAnyOrder (there is an implicit inAnyOrder at the top level). Calls to inSequence and inAnyOrder can be arbitrarily nested. For example:

m expects 'a
inSequence {
  m expects 'b
  inAnyOrder {
    m expects 'c
    inSequence {
      m expects 'd
      m expects 'e
    }
    m expects 'f
  }
  m expects 'g

Debugging

If faced with a difficult to debug failing expectation, consider mixing one or both of the VerboseErrors or CallLogging traits into your test suite:

class MyTest extends Suite with MockFactory with VerboseErrors with CallLogging
Visibility
  1. Public
  2. All

Type Members

  1. trait AbstractMockFactory extends ProxyMockFactory

  2. trait CallLogging extends AnyRef

    Trait that can be mixed into a AbstractMockFactory to switch on call logging.

  3. trait ClassLoaderStrategy extends AnyRef

  4. class Expectation extends Handler

    Represents a single expectation

  5. case class ExpectationException (msg: String) extends RuntimeException with Product with Serializable

  6. class MatchAny extends Equals

  7. class MatchEpsilon extends Equals

  8. trait Mock extends AnyRef

  9. class MockFunction extends AnyRef

    Attributes
    abstract
  10. class MockFunction0 [R] extends MockFunction with () ⇒ R

  11. class MockFunction1 [T1, R] extends MockFunction with (T1) ⇒ R

  12. class MockFunction10 [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] extends MockFunction with (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) ⇒ R

  13. class MockFunction2 [T1, T2, R] extends MockFunction with (T1, T2) ⇒ R

  14. class MockFunction3 [T1, T2, T3, R] extends MockFunction with (T1, T2, T3) ⇒ R

  15. class MockFunction4 [T1, T2, T3, T4, R] extends MockFunction with (T1, T2, T3, T4) ⇒ R

  16. class MockFunction5 [T1, T2, T3, T4, T5, R] extends MockFunction with (T1, T2, T3, T4, T5) ⇒ R

  17. class MockFunction6 [T1, T2, T3, T4, T5, T6, R] extends MockFunction with (T1, T2, T3, T4, T5, T6) ⇒ R

  18. class MockFunction7 [T1, T2, T3, T4, T5, T6, T7, R] extends MockFunction with (T1, T2, T3, T4, T5, T6, T7) ⇒ R

  19. class MockFunction8 [T1, T2, T3, T4, T5, T6, T7, T8, R] extends MockFunction with (T1, T2, T3, T4, T5, T6, T7, T8) ⇒ R

  20. class MockFunction9 [T1, T2, T3, T4, T5, T6, T7, T8, T9, R] extends MockFunction with (T1, T2, T3, T4, T5, T6, T7, T8, T9) ⇒ R

  21. trait ProxyMockFactory extends AnyRef

  22. trait VerboseErrors extends AnyRef

    Trait that can be mixed into a AbstractMockFactory to switch on verbose error messages.

Value Members

  1. object MatchEpsilon extends AnyRef

  2. object threadContextClassLoaderStrategy extends ClassLoaderStrategy