Skip to content

Commit

Permalink
Merge pull request #6 from sirbrillig/add/phpunit-custom-assertions
Browse files Browse the repository at this point in the history
Add PHPUnit custom assertions
  • Loading branch information
sirbrillig authored Jul 27, 2016
2 parents b6214a7 + 5861c44 commit ca26c79
Show file tree
Hide file tree
Showing 16 changed files with 655 additions and 16 deletions.
25 changes: 25 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,28 @@
- `once()`: Alias for `times( 1 )`.
- `twice()`: Alias for `times( 2 )`.
- `before( $spy )`: Add an expected behavior that the spy was called before $spy.

# PHPUnit Custom Assertions

These are methods available on instances of `\Spies\TestCase`.

### Constraints for `assertThat()`

- `wasCalled()`
- `wasNotCalled()`
- `wasCalledTimes( $count )`
- `wasCalledBefore( $spy )`
- `wasCalledWhen( $callable )`

### Assertions

- `assertSpyWasCalled( $spy )`
- `assertSpyWasNotCalled( $spy )`
- `assertSpyWasCalledWith( $spy, $args )`
- `assertSpyWasNotCalledWith( $spy, $args )`
- `assertSpyWasCalledTimes( $spy, $count )`
- `assertSpyWasNotCalledTimes( $spy, $count )`
- `assertSpyWasCalledBefore( $spy, $other_spy )`
- `assertSpyWasNotCalledBefore( $spy, $other_spy )`
- `assertSpyWasCalledWhen( $spy, $callable )`
- `assertSpyWasNotCalledWhen( $spy, $callable )`
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,29 @@ function test_calculation() {
add_together( 2, 3 );
}
```

## PHPUnit Custom Assertions

If you prefer to use PHPUnit custom assertions rather than Expectations, those are also available (although you must base your test class on `\Spies\TestCase`):

```php
class MyTest extends \Spies\TestCase {
function test_spy_is_called_correctly() {
$spy = \Spies\make_spy();
$spy( 'hello', 'world', 7 );
$spy( 'hello', 'world', 8 );
$this->assertSpyWasCalledWith( 'hello', 'world', \Spies\any() );
}
}
```

Custom assertions will provide detailed information about why your test failed, which is much better than "false is not true".

```
Failed asserting that a spy is called with arguments: ( "a", "b", "c" ).
a spy was actually called with:
1. arguments: ( "b", "b", "c" ),
2. arguments: ( "m", "b", "c" )
```

See the [API document](API.md) for the full list of custom assertions available.
21 changes: 21 additions & 0 deletions src/Spies/ArgumentFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
namespace Spies;

class ArgumentFormatter {
private $args;

public function __construct( $args ) {
$this->args = $args;
}

public function __toString() {
if ( empty( $this->args ) ) {
return 'no arguments';
}
return 'arguments: ( ' . $this->get_args_as_array() . ' )';
}

private function get_args_as_array() {
return implode( ', ', array_map( 'json_encode', $this->args ) );
}
}
56 changes: 56 additions & 0 deletions src/Spies/FailureGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php
namespace Spies;

class FailureGenerator {
private $messages = [];

public function add_message( $message ) {
$this->messages[] = $message;
}

public function get_message() {
return implode( ' ', $this->messages );
}

public function spy_was_not_called( $spy ) {
$this->add_message( $spy->get_function_name() . ' is called' );
}

public function spy_was_called( $spy ) {
$this->add_message( $spy->get_function_name() . ' is not called' );
}

public function spy_was_not_called_with( $spy, $args ) {
$this->spy_was_not_called( $spy );
$desc = 'with ';
$desc .= strval( new ArgumentFormatter( $args ) );
$this->add_message( $desc );
}

public function spy_was_not_called_with_additional( $spy ) {
$desc = $spy->get_function_name() . ' was actually ';
$calls = $spy->get_called_functions();
$desc .= empty( $calls ) ? 'not called at all' : 'called with:' . strval( new SpyCallFormatter( $calls ) );
$this->add_message( $desc );
}

public function spy_was_not_called_times( $spy, $count ) {
$this->spy_was_not_called( $spy );
$desc = $count . ' ';
$desc .= $count === 1 ? 'time' : 'times';
$this->add_message( $desc );
}

public function spy_was_not_called_before( $spy, $target_spy ) {
$this->spy_was_not_called( $spy );
$desc = 'before ' . $target_spy->get_function_name();
$this->add_message( $desc );
}

public function spy_was_not_called_when( $spy ) {
$this->spy_was_not_called( $spy );
$desc = 'with arguments matching the provided function';
$this->add_message( $desc );
}

}
27 changes: 27 additions & 0 deletions src/Spies/SpiesConstraintWasCalled.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
namespace Spies;

class SpiesConstraintWasCalled extends \PHPUnit_Framework_Constraint {
public function matches( $other ) {
if ( ! $other instanceof \Spies\Spy ) {
return false;
}
return $other->was_called();
}

public function failureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called( $other );
return $generator->get_message();
}

protected function additionalFailureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called_with_additional( $other );
return $generator->get_message();
}

public function toString() {
return '';
}
}
30 changes: 30 additions & 0 deletions src/Spies/SpiesConstraintWasCalledBefore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
namespace Spies;

class SpiesConstraintWasCalledBefore extends \PHPUnit_Framework_Constraint {
private $target_spy;

public function __construct( $target_spy ) {
parent::__construct();
$this->target_spy = $target_spy;
}

public function matches( $other ) {
if ( ! $other instanceof \Spies\Spy ) {
return false;
}
return $other->was_called_before( $this->target_spy );
}

public function failureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called_before( $other, $this->target_spy );
return $generator->get_message();
}

public function toString() {
return '';
}
}


35 changes: 35 additions & 0 deletions src/Spies/SpiesConstraintWasCalledTimes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
namespace Spies;

class SpiesConstraintWasCalledTimes extends \PHPUnit_Framework_Constraint {
private $count;

public function __construct( $count ) {
parent::__construct();
$this->count = $count;
}

public function matches( $other ) {
if ( ! $other instanceof \Spies\Spy ) {
return false;
}
return $other->was_called_times( $this->count );
}

public function failureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called_times( $other, $this->count );
return $generator->get_message();
}

protected function additionalFailureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called_with_additional( $other );
return $generator->get_message();
}

public function toString() {
return '';
}
}

35 changes: 35 additions & 0 deletions src/Spies/SpiesConstraintWasCalledWhen.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
namespace Spies;

class SpiesConstraintWasCalledWhen extends \PHPUnit_Framework_Constraint {
private $expected_callable;

public function __construct( $callable ) {
parent::__construct();
$this->expected_callable = $callable;
}

public function matches( $other ) {
if ( ! $other instanceof \Spies\Spy ) {
return false;
}
return $other->was_called_when( $this->expected_callable );
}

protected function failureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called_when( $other );
return $generator->get_message();
}

protected function additionalFailureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called_with_additional( $other );
return $generator->get_message();
}

public function toString() {
return '';
}
}

35 changes: 35 additions & 0 deletions src/Spies/SpiesConstraintWasCalledWith.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
namespace Spies;

class SpiesConstraintWasCalledWith extends \PHPUnit_Framework_Constraint {
private $expected_args;

public function __construct( $args ) {
parent::__construct();
$this->expected_args = $args;
}

public function matches( $other ) {
if ( ! $other instanceof \Spies\Spy ) {
return false;
}
return $other->was_called_with_array( $this->expected_args );
}

protected function failureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called_with( $other, $this->expected_args );
return $generator->get_message();
}

protected function additionalFailureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_not_called_with_additional( $other );
return $generator->get_message();
}

public function toString() {
return '';
}
}

22 changes: 22 additions & 0 deletions src/Spies/SpiesConstraintWasNotCalled.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
namespace Spies;

class SpiesConstraintWasNotCalled extends \PHPUnit_Framework_Constraint {
public function matches( $other ) {
if ( ! $other instanceof \Spies\Spy ) {
return false;
}
return ! $other->was_called();
}

public function failureDescription( $other ) {
$generator = new FailureGenerator();
$generator->spy_was_called( $other );
return $generator->get_message();
}

public function toString() {
return '';
}
}

Loading

0 comments on commit ca26c79

Please sign in to comment.