Skip to content

Commit

Permalink
Merge pull request #14 from sirbrillig/add/internal-function-test
Browse files Browse the repository at this point in the history
Allow returning falsey values in mocked internal functions
  • Loading branch information
sirbrillig authored Aug 9, 2017
2 parents aaf082e + 85e33d6 commit 0da4424
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 10 deletions.
3 changes: 3 additions & 0 deletions patchwork.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"redefinable-internals": [ "trigger_error", "count" ]
}
14 changes: 14 additions & 0 deletions src/Spies/FalseyValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Spies;

// A special case for falsey return values
class FalseyValue {
public function __construct( $value ) {
$this->value = $value;
}

public function get_value() {
return $this->value;
}
}
15 changes: 11 additions & 4 deletions src/Spies/GlobalSpies.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ public static function get_global_spy( $function_name ) {
*
* You should not need to call this directly.
*/
public static function handle_call_for( $function_name, $args ) {
public static function handle_call_for( $function_name, $args, $options = [] ) {
$spy = self::get_global_spy( $function_name );
if ( ! isset( $spy ) ) {
throw new UndefinedFunctionException( 'Call to undefined function ' . $function_name );
}
return $spy->call_with_array( $args );
return $spy->call_with_array( $args, $options );
}

/**
Expand Down Expand Up @@ -117,14 +117,21 @@ private static function replace_global_function( $function_name ) {
return;
}
self::$redefined_functions[ $function_name ] = \Patchwork\redefine( $function_name, function() use ( $function_name ) {
$value = \Spies\GlobalSpies::handle_call_for( $function_name, func_get_args() );
$value = \Spies\GlobalSpies::handle_call_for( $function_name, func_get_args(), [ 'return_falsey_objects' => true ] );
if ( isset( $value ) ) {
return $value;
return self::filter_return_for( $value );
}
return \Patchwork\relay();
} );
}

private static function filter_return_for( $return ) {
if ( $return instanceof FalseyValue ) {
return $return->get_value();
}
return $return;
}

private static function generate_function_with( $function_name ) {
$namespace_text = '';
$function_name_text = $function_name;
Expand Down
20 changes: 14 additions & 6 deletions src/Spies/Spy.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,12 @@ public function call() {
* Same as `call`, but with an array of arguments instead of any number.
*
* @param array $args Any arguments for the function in an array
* @param array $options Special options for the call
* @return mixed Whatever the mocked function returns
*/
public function call_with_array( $args ) {
public function call_with_array( $args, $options = [] ) {
$this->record_function_call( $args );
return $this->get_return_for( $args );
return $this->get_return_for( $args, $options );
}

/**
Expand Down Expand Up @@ -214,6 +215,10 @@ public function that_returns( $value ) {
* @return Spy This Spy
*/
public function and_return( $value ) {
// Add a special case for falsey return values so we can detect them
if ( ! $value ) {
$value = new FalseyValue( $value );
}
if ( isset( $this->with_arguments ) ) {
$this->conditional_returns[] = [ 'args' => $this->with_arguments, 'return' => $value ];
$this->with_arguments = null;
Expand Down Expand Up @@ -376,7 +381,7 @@ private function record_function_call( $args ) {
*
* You should not need to call this directly.
*/
private function get_return_for( $args ) {
private function get_return_for( $args, $options = [] ) {
if ( $this->conditional_returns ) {
$conditional_return = array_reduce( $this->conditional_returns, function( $carry, $condition ) use ( $args ) {
if ( Helpers::do_args_match( $condition['args'], $args ) ) {
Expand All @@ -385,26 +390,29 @@ private function get_return_for( $args ) {
return $carry;
} );
if ( isset( $conditional_return ) ) {
return $this->filter_return_for( $conditional_return, $args );
return $this->filter_return_for( $conditional_return, $args, $options );
}
}
if ( isset( $this->return_value ) ) {
$return = $this->return_value;
return $this->filter_return_for( $return, $args );
return $this->filter_return_for( $return, $args, $options );
}
if ( isset( $this->function_name ) && is_callable( $this->function_name ) ) {
return \Spies\GlobalSpies::call_original_global_function( $this->function_name, $args );
}
return null;
}

private function filter_return_for( $return, $args ) {
private function filter_return_for( $return, $args, $options = [] ) {
if ( $return instanceof PassedArgument ) {
return $args[ $return->index ];
}
if ( is_callable( $return ) ) {
return call_user_func_array( $return, $args );
}
if ( $return instanceof FalseyValue && empty( $options['return_falsey_objects'] ) ) {
return $return->get_value();
}
return $return;
}
}
Expand Down
30 changes: 30 additions & 0 deletions tests/SpyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,36 @@ public function test_spy_was_called_times_with_returns_false_if_not_called_with_
$this->assertFalse( $spy->was_called_times_with( 2, 'a', 'b' ) );
}

public function test_stub_on_internal_existing_method_replaces_return_value_of_existing_method() {
\Spies\mock_function( 'trigger_error' )->and_return( 'boo' );
$this->assertEquals( 'boo', trigger_error( 'foo' ) );
}

public function test_stub_on_internal_existing_method_replaces_return_value_of_existing_method_if_empty() {
\Spies\mock_function( 'trigger_error' )->and_return( [] );
$this->assertEquals( [], trigger_error( 'foo' ) );
}

public function test_stub_on_internal_existing_method_replaces_return_value_of_existing_method_if_null() {
\Spies\mock_function( 'trigger_error' )->and_return( null );
$this->assertEquals( null, trigger_error( 'foo' ) );
}

public function test_stub_on_internal_existing_method_with_conditions_replaces_return_value_of_existing_method() {
\Spies\mock_function( 'trigger_error' )->when_called->with( 'foo' )->and_return( 'boo' );
$this->assertEquals( 'boo', trigger_error( 'foo' ) );
}

public function test_stub_on_internal_existing_method_with_conditions_replaces_return_value_of_existing_method_if_null() {
\Spies\mock_function( 'trigger_error' )->when_called->with( 'foo' )->and_return( null );
$this->assertEquals( null, trigger_error( 'foo' ) );
}

public function test_spy_on_internal_existing_method_calls_original_method_when_calling_spy() {
$spy = \Spies\get_spy_for( 'count' );
$this->assertEquals( 2, $spy( [ 'a', 'b' ] ) );
}

public function test_stub_on_existing_method_replaces_return_value_of_spy() {
$spy = \Spies\mock_function( 'globalFunctionFoo' )->and_return( 'boo' );
$this->assertEquals( 'boo', $spy() );
Expand Down

0 comments on commit 0da4424

Please sign in to comment.