Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Update mockfor tests to demonstrate problems with mockfor. Added stubfor tests.

...

For an extensive list of usages see the unit tests that show how to use the mock package.

MockFor

...

Tests

Some tests to demonstrate how to use the MockFor and StubFor classes

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 most return the proxy
   * instance instead.
   * 
   * NOTE: MockFor constructs an instance of the real class when
   * creating the proxy instance. This will require that the
   * required parameters are passed in to the 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
  }
}

StubFor Tests

Some tests to demonstrate how to use the StuvFor classes

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"
  }
}
Code Block