Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

In principle, it is used like this:

Code Block

import groovy.mock.interceptor.MockFor

def mocker = new MockFor(Collaborator.class)  // create the Mock support
mocker.demand.one(1..2) { 1 }                 // demand the 'one' method one or two times, returning 1
mocker.demand.two()     { 2 }                 // demand the 'two' method exactly once, returning 2
mocker.use {                                  // start using the Mock
    def caller = new Caller()                 // caller will call Collaborator
    assertEquals 1, caller.collaborateOne()   // will call Collaborator.one
    assertEquals 1, caller.collaborateOne()   // will call Collaborator.one
    assertEquals 2, caller.collaborateTwo()   // will call Collaborator.two
}                                             // implicit verify for strict expectation here

...

Some tests to demonstrate how to use the MockFor classesclass.

Code Block

import grails.test.*
import groovy.mock.interceptor.*


class MockForTests extends GroovyTestCase {

  void test_mock_single_method_and_use_as_category() {
    def mock = new MockFor(MockForTestsClass)
    mock.demand.amethod { "from mock"}
    mock.use {
      assertEquals "from mock", new MockForTestsClass().amethod()
    }
  }

  void test_mock_single_method_using_demand_with_and_use_as_category() {
    def mock = new MockFor(MockForTestsClass)
    mock.demand.with {
      amethod() { "from mock"}
    }

    mock.use {
      assertEquals "from mock", new MockForTestsClass().amethod()
    }
  }

  void test_mock_single_static_method_and_use_as_category() {
    def mock = new MockFor(MockForTestsClass)
    mock.demand.astaticmethod { "from static mock"}
    mock.use {
      assertEquals "from static mock", MockForTestsClass.astaticmethod()
    }
  }

  void test_mock_method_with_parameters() {
    def mock = new MockFor(MockForTestsClass)
    mock.demand.amethodwithparameters { a -> "from mock with parameters ${a}" }

    mock.use {
      assertEquals "from mock with parameters 123",
          new MockForTestsClass().amethodwithparameters(123)
    }
  }

  /*/    * This is a special case problem related to a bug. This proves the work around.
  // * The bug is that you can not mock the "find" method unless you pass in the
  // * cardinality as well.
   */
  void test_mocking_a_method_called_find_is_a_special_case() {
    def mock = new MockFor(MockForTestsClass)
    mock.demand.find(1) { "cardinality 1" }
    mock.use {
      assertEquals "cardinality 1", MockForTestsClass.find()
    }
  }

  /*/    * This illustrates a down side of MockFor. Despite the fact that the MockFor constructor
  // * is passed the type that it is replacing it does no verification that the demands actually
  // * match the signatures of the methods in the real implementation.
   */
  void test_methods_do_not_have_to_exist_on_a_class_for_them_to_be_mocked() {

    def mock = new MockFor(Object)
    mock.demand.aMethodThatDoesNotExist { "SEE IT WORKS ANYWAY" }

    mock.use {
      assert "SEE IT WORKS ANYWAY" == new Object().aMethodThatDoesNotExist()
    }
  }

  /*/    * This illustrates a down side of MockFor. Despite the fact that the MockFor constructor
  // * is passed the type that it is replacing it does no verification that the demands actually
  // * match the signatures of the methods in the real implementation.
   */
  void test_demand_the_wrong_number_of_arguments_is_allowed() {
    def mock = new MockFor(MockForTestsClass)
    mock.demand.amethodwithparameters { a, extraArg -> "blindly called, changes in implementation ignored" }

    mock.use {
      assertEquals "blindly called, changes in implementation ignored",
          new MockForTestsClass().amethodwithparameters(123, 123)
    }
  }

  void test_mock_single_method_and_use_proxy() {
    def mock = new MockFor(MockForTestsClass)
    mock.demand.amethod { "from mock with proxy"}

    def proxy = mock.proxyInstance()
    assertEquals "from mock with proxy", proxy.amethod()
    mock.verify proxy
  }

  void test_create_proxy_instance_with_constructor_arguments() {
    def mock = new MockFor(MockForTestClassWithConstructorArgs)
    mock.demand.amethod { "from mock with proxy"}

    def proxy = mock.proxyInstance(["value1", "value2"]as Object[])
    assertEquals "from mock with proxy", proxy.amethod()
    mock.verify proxy
  }

  void test_mocking_a_constructor_call() {
    def mock = new MockFor(MockForTestsClass, true)
    mock.demand.with {
      MockForTestsClass() {  throw new Exception("YES IT WORKED") }
    }

    mock.use {
      shouldFail Exception, { new MockForTestsClass() }
    }
  }

  /*/    * Constructor mocking requires that an instance of a class is returned
  // * and that instance can be cast to the mocked type.
   */
  void test_mock_a_constructor_that_gets_used() {
    def mock = new MockFor(MockForTestsClass, true)
    mock.demand.with {
      MockForTestsClass() { new Object() as GroovyObject }
    }

    mock.use {  new MockForTestsClass() }
  }

  /*/    * If you return the wrong type from this mocked constructor you will get
  // * an error like this which is a little hard to work out.:
  //
*  //  *    java.lang.ClassCastException: java.lang.String cannot be cast to groovy.lang.GroovyObject
   *//
  void test_mock_a_constructor_that_returns_the_wrong_type() {
    def mock = new MockFor(MockForTestsClass, true)
    mock.demand.with {
      MockForTestsClass() { "THIS IS THE WRONG TYPE BEING RETURNED" }
    }

    shouldFail ClassCastException, {
      mock.use {  new MockForTestsClass() }
    }
  }

  /*/    * To demand more interactions with the object beyond the constructor the mocked 
* constructor the// mocked constructor most return the proxy    * instance instead.
  //
*  //  * NOTE: MockFor constructs an instance of the real class when creating the proxy 
* creating the// proxy instance. This will require that the    * required parameters are passed in to thethe 
proxyInstance  // proxyInstance * call for forwarding to the real constructor.
   */
  void test_mock_a_constructor_that_returns_a_proxy_instance() {
    def mock = new MockFor(MockForTestsClass, true)

    def theProxyInstance = mock.proxyInstance()

    mock.demand.with {
      MockForTestsClass() { theProxyInstance }
      amethod() { }
    }

    mock.use {
      def testClass= new MockForTestsClass()
      testClass.amethod()
    }
  }
}

class MockForTestsClass {

  void amethod() {
    println "original method"
  }

  static void astaticmethod() {
    println "original static method"
  }

  void amethodwithparameters(message) {
    println "original method with parameters"
  }
}

class MockForTestClassWithConstructorArgs extends MockForTestsClass {
  def arg1
  def arg2
  MockForTestClassWithConstructorArgs(arg1, arg2) {
    this.arg1 = arg1
    this.arg2 = arg2
  }
}

...

Some tests to demonstrate how to use the StuvFor classesStubFor class.

Code Block

import groovy.mock.interceptor.*

class StubForTest extends GroovyTestCase {

  void test_stub_single_method_no_different_from_mock() {
    def mock = new StubFor(StubForTestsClass)
    mock.demand.amethod { "from stub" }
    mock.use {
      assertEquals "from stub", new StubForTestsClass().amethod()
    }
  }

  /*/    * While we can call stubbed methods many times we still have to specify
  // * a cardinality higher than our expected call count.
   */
  void test_stub_single_method_but_call_it_many_times() {
    def mock = new StubFor(StubForTestsClass)
    mock.demand.amethod(9999) { "from stub" }
    mock.use {
      assertEquals "from stub", new StubForTestsClass().amethod()
      assertEquals "from stub", new StubForTestsClass().amethod()
      assertEquals "from stub", new StubForTestsClass().amethod()
      assertEquals "from stub", new StubForTestsClass().amethod()
      assertEquals "from stub", new StubForTestsClass().amethod()
      assertEquals "from stub", new StubForTestsClass().amethod()
    }
  }

  void test_stub_single_method_dont_call_it_and_expect_no_errors() {
    def mock = new StubFor(StubForTestsClass)
    mock.demand.amethod { "not called" }
    mock.use { new StubForTestsClass() }
  }
}

class StubForTestsClass {
  void amethod() {
    println "original method"
  }
}