Browse files

Add MockerChain to make assertions easier and extendible.

  • Loading branch information...
1 parent 77a0f46 commit 8c138f6844554c82d98c1ba4b91d776afd223311 Blaine Schmeisser committed Jan 17, 2013
Showing with 351 additions and 0 deletions.
  1. +16 −0 test/Mocker.php
  2. +196 −0 test/MockerChain.php
  3. +127 −0 tests/cases/test/MockerChainTest.php
  4. +4 −0 tests/cases/test/MockerTest.php
  5. +8 −0 tests/mocks/test/MockStdClass.php
View
16 test/Mocker.php
@@ -403,6 +403,22 @@ protected static function _validateMockee($mockee) {
return true;
}
+ /**
+ * Generate a chain class with the current rules of the mock.
+ *
+ * @param object $mock Mock to chain
+ * @return object MockerChain instance
+ */
+ public static function chain($mock) {
+ $results = array();
+ if (is_object($mock) && isset($mock->results)) {
+ $results = $mock->results;
+ } elseif (is_string($mock) && class_exists($mock) && isset($mock::$results)) {
+ $results = $mock::$results;
+ }
+ return new MockerChain($results);
+ }
+
}
?>
View
196 test/MockerChain.php
@@ -0,0 +1,196 @@
+<?php
+/**
+ * Lithium: the most rad php framework
+ *
+ * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
+ * @license http://opensource.org/licenses/bsd-license.php The BSD License
+ */
+
+namespace lithium\test;
+
+/**
+ * Mocker chain is used to aid in assertion of method calls.
+ *
+ * Asserting if `method1` was not called
+ * {{{
+ * $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ * $this->assertFalse(Mocker::chain($mock)->called('method1')->success());
+ * }}}
+ *
+ * Asserting if `method1` was called 2 times
+ * {{{
+ * $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ * $this->assertTrue(Mocker::chain($mock)->called('method1')->eq(2)->success());
+ * }}}
+ *
+ * Asserting if `method2` was called after `method1`
+ * {{{
+ * $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ * $this->assertTrue(Mocker::chain($mock)->called('method1')->called('method2')->success());
+ * }}}
+ *
+ * Asserting if `method2` was called after `method1`, and `method2` had specific arguments.
+ * {{{
+ * $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ * $this->assertTrue(Mocker::chain($mock)
+ * ->called('method1')
+ * ->called('method2')->with('foo', 'bar')
+ * ->success());
+ * }}}
+ */
+class MockerChain extends \lithium\core\Object {
+
+ /**
+ * Data to be used in the class.
+ *
+ * `results` Cached mock results
+ * `method` Method we are asserting
+ * `args` Args we are asserting
+ * `success` Success flag
+ * `callTime` Last method call
+ *
+ * @var array
+ */
+ protected $_data = array(
+ 'results' => null,
+ 'method' => false,
+ 'args' => false,
+ 'success' => true,
+ 'callTime' => 0,
+ );
+
+ /**
+ * Saves the results from the mock.
+ *
+ * @param array $results Results from the mock
+ */
+ public function __construct($results) {
+ $this->_data['results'] = $results;
+ }
+
+ /**
+ * Validates that a given methodis called a set number of times.
+ *
+ * @param string $comparison Comparison type 'gt', 'gte', 'lt', 'lte', or 'eq'.
+ * @param array $args The first argument is the expected result.
+ * @return object
+ */
+ public function __call($comparison, $args) {
+ $methodExists = in_array($comparison, array('gt', 'gte', 'lt', 'lte', 'eq'), true);
+ if (!$this->_data['success'] || !$methodExists) {
+ return $this;
+ }
+ if (count($args) === 0 || !is_int($args[0])) {
+ $this->_data['success'] = false;
+ return $this;
+ }
+ $result = 0;
+ $expected = $args[0];
+ $method = $this->_data['method'];
+ $args = $this->_data['args'];
+ foreach ($this->_data['results'][$method] as $call) {
+ $correctTime = $this->_data['callTime'] <= $call['time'];
+ $correctArgs = !is_array($args) || $args === $call['args'];
+ if ($correctTime && $correctArgs) {
+ $this->_data['callTime'] = $call['time'];
+ $result++;
+ }
+ }
+ switch ($comparison) {
+ case 'gt':
+ $this->_data['success'] = $result > $expected;
+ break;
+ case 'gte':
+ $this->_data['success'] = $result >= $expected;
+ break;
+ case 'lt':
+ $this->_data['success'] = $result < $expected;
+ break;
+ case 'lte':
+ $this->_data['success'] = $result <= $expected;
+ break;
+ case 'eq':
+ $this->_data['success'] = $result === $expected;
+ break;
+ }
+ return $this;
+ }
+
+ /**
+ * Valides the method was called after the last call.
+ *
+ * @param string $method Method to assert
+ * @return object
+ */
+ function called($method) {
+ if (!$this->_data['success']) {
+ return $this;
+ }
+
+ $this->_data['method'] = $method;
+ $this->_data['args'] = false;
+ if (!isset($this->_data['results'][$method])) {
+ $this->_data['success'] = false;
+ return $this;
+ }
+
+ foreach ($this->_data['results'][$method] as $call) {
+ if ($this->_data['callTime'] < $call['time']) {
+ $this->_data['callTime'] = $call['time'];
+ return $this;
+ }
+ }
+
+ $this->_data['success'] = false;
+ return $this;
+ }
+
+ /**
+ * Will further narrow down the original 'called' method.
+ *
+ * Valides the cached method name was called with these args
+ *
+ * @param mixed $arg,... Optional arguments to test against
+ * @return object
+ */
+ public function with() {
+ if (!$this->_data['success']) {
+ return $this;
+ }
+
+ $method = $this->_data['method'];
+ $this->_data['args'] = $args = func_get_args();
+
+ foreach ($this->_data['results'][$method] as $call) {
+ $correctTime = $this->_data['callTime'] <= $call['time'];
+ $correctArgs = $args === $call['args'];
+ if ($correctTime && $correctArgs) {
+ $this->_data['callTime'] = $call['time'];
+ return $this;
+ }
+ }
+
+ $this->_data['success'] = false;
+ return $this;
+ }
+
+ /**
+ * Gives back the success flag
+ *
+ * @return bool
+ */
+ public function success() {
+ $success = $this->_data['success'];
+ $this->_data = array(
+ 'results' => $this->_data['results'],
+ 'method' => false,
+ 'args' => false,
+ 'success' => true,
+ 'callTime' => 0,
+ );
+ return $success;
+ }
+
+}
+
+?>
View
127 tests/cases/test/MockerChainTest.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * Lithium: the most rad php framework
+ *
+ * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
+ * @license http://opensource.org/licenses/bsd-license.php The BSD License
+ */
+
+namespace lithium\tests\cases\test;
+
+use lithium\test\Mocker;
+
+class MockerChainTest extends \lithium\test\Unit {
+
+ public function setUp() {
+ Mocker::register();
+ }
+
+ public function testStartSuccessful() {
+ $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ $chain = Mocker::chain($mock);
+
+ $this->assertTrue($chain->success());
+ }
+
+ public function testBasicNotCalled() {
+ $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ $chain = Mocker::chain($mock);
+
+ $this->assertFalse($chain->called('method1')->success());
+ }
+
+ public function testBasicCalled() {
+ $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ $mock->method1();
+ $chain = Mocker::chain($mock);
+
+ $this->assertTrue($chain->called('method1')->success());
+ }
+
+ public function testCalledWith() {
+ $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ $mock->method1('foo');
+ $chain = Mocker::chain($mock);
+
+ $this->assertTrue($chain->called('method1')->success());
+ $this->assertFalse($chain->called('method1')->with('bar')->success());
+ $this->assertTrue($chain->called('method1')->with('foo')->success());
+ $this->assertFalse($chain->called('method1')->with('foo', 'bar')->success());
+ }
+
+ public function testMethodCalledBefore() {
+ $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ $mock->method1();
+ $mock->method2();
+ $mock->method1();
+ $chain = Mocker::chain($mock);
+
+ $this->assertTrue($chain->called('method1')
+ ->called('method2')
+ ->called('method1')
+ ->success());
+ $this->assertFalse($chain->called('method2')
+ ->called('method1')
+ ->called('method1')
+ ->success());
+ }
+
+ public function testMethodWithParamsCalledBefore() {
+ $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ $mock->method1('foo');
+ $mock->method2('bar');
+ $mock->method1('baz');
+ $chain = Mocker::chain($mock);
+
+ $this->assertTrue($chain->called('method1')
+ ->called('method2')->with('bar')
+ ->called('method1')
+ ->success());
+ $this->assertFalse($chain->called('method1')->with('bar')
+ ->called('method2')->with('bar')
+ ->called('method1')
+ ->success());
+ $this->assertFalse($chain->called('method1')
+ ->called('method2')->with('bar')
+ ->called('method1')->with('bar')
+ ->success());
+ }
+
+ public function testMethodCalledSpecificTimes() {
+ $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ $mock->method1();
+ $mock->method2();
+ $mock->method1();
+ $chain = Mocker::chain($mock);
+
+ $this->assertFalse($chain->called('method2')->eq(2)->success());
+ $this->assertTrue($chain->called('method1')->eq(2)->success());
+ $this->assertTrue($chain->called('method1')->gt(0)->success());
+ $this->assertTrue($chain->called('method1')->gte(1)->success());
+ $this->assertTrue($chain->called('method1')->lt(3)->success());
+ $this->assertTrue($chain->called('method1')->lte(2)->success());
+ $this->assertFalse($chain->called('method1')->lte(1)->success());
+ }
+
+ public function testMultipleCallsWithArgsAndSpecificCalled() {
+ $mock = new \lithium\tests\mocks\test\mockStdClass\Mock();
+ $mock->method1('foo', 'bar');
+ $mock->method1('foo', 'bar');
+ $mock->method1('foo', 'bar');
+ $mock->method2('baz');
+ $mock->method2('baz');
+ $mock->method1();
+ $chain = Mocker::chain($mock);
+
+ $this->assertTrue($chain->called('method1')->with('foo', 'bar')->eq(3)->success());
+ $this->assertTrue($chain->called('method2')->with('baz')->eq(2)->success());
+ $this->assertTrue($chain->called('method1')->with()->eq(1)->success());
+
+ $this->assertTrue($chain->called('method1')->with('foo', 'bar')->eq(3)
+ ->called('method2')->with('baz')->eq(2)
+ ->called('method1')->with()->eq(1)->success());
+ }
+
+}
+
+?>
View
4 tests/cases/test/MockerTest.php
@@ -198,6 +198,10 @@ public function testGetByReference() {
$this->assertIdentical('bar', $stdObj->foo);
}
+ public function testChainReturnsMockerChain() {
+ $this->assertTrue(Mocker::chain(new \stdClass) instanceof \lithium\test\MockerChain);
+ }
+
}
?>
View
8 tests/mocks/test/MockStdClass.php
@@ -28,6 +28,14 @@ public function filterableData() {
return $this->_data;
}
+ public function method1() {
+ return true;
+ }
+
+ public function method2() {
+ return false;
+ }
+
}
?>

0 comments on commit 8c138f6

Please sign in to comment.