2017 © Pedro Peláez
 

library function-mocker-le

A lightweight function mocking solution.

image

lucatume/function-mocker-le

A lightweight function mocking solution.

  • Tuesday, March 6, 2018
  • by theAverageDev
  • Repository
  • 0 Watchers
  • 1 Stars
  • 6,775 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 0 Forks
  • 0 Open issues
  • 4 Versions
  • 51 % Grown

The README.md

Function Mocker, Light Edition

When function-mocker is overkill., (*1)

Code example

<?php

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Assert;
use function tad\FunctionMockerLe\define;
use function tad\FunctionMockerLe\defineWithMap;
use function tad\FunctionMockerLe\defineWithValueMap;
use function tad\FunctionMockerLe\defineAll;
use function tad\FunctionMockerLe\undefineAll;

class MyOrmTest extends TestCase {

    public function test_query_parameters_are_preserved(){
        // define a non existing function
        define('get_posts', function(array $args){
            Assert::assertEquals(['post_type' => 'book','paginated' => 2, 'posts_per_page' => 6], $args);

            return [];
        });

        $orm = new MyOrm('book');

        $orm->query()
            ->page(2)
            ->perPage(6)
            ->fetch();
    }

    public function test_users_cannot_fetch_posts_they_cannot_read(){
        // define a number of functions using a <function> => <callback> map
        defineWithMap([
            'is_user_logged_in' =>  function(){
                return true;
            },
            'get_posts' => function(){
                return[
                    ['ID' => 1, 'post_title' => 'One'],
                    ['ID' => 2, 'post_title' => 'Two'],
                    ['ID' => 3, 'post_title' => 'Three'],
                    ['ID' => 4, 'post_title' => 'Four']
                ];
            },
        'current_user_can' => function($cap, $id){
                // a post ID to 'can' map
                $map  = [
                    1 => true,
                    2 => true,
                    3 => false,
                    4 => true
                ];

                return $map($id);
                }
        ]);

        $orm = new MyOrm('book');

        $books = $orm->query()
            ->fetch();

        $expected = [
                ['ID' => 1, 'post_title' => 'One'],
                ['ID' => 2, 'post_title' => 'Two'],
                ['ID' => 4, 'post_title' => 'Four']
        ];

        $this->assertEquals($expected, $books);
    }

    public function test_sticky_posts_are_not_prepended_if_not_home_archive_or_tag(){
        // define a number of functions all with the same callback...
        defineAll(['is_home', 'is_archive', 'is_tag'], function(){
            return false;
        });
        define('get_posts', function(array $args){
            Assert::arrayHasKey('ignore_sticky_posts,', $args);
            Assert::assertEquals('1', $args['ignore_sticky_posts']);
        });

        $orm = new MyOrm('book');
        $orm->query()->fetch();

        // ...and then redefine them
        defineWithValueMap([
            'is_home' => true,
            'is_archive' => false,
            'is_tag' => false
        ]);

        // redefine as needed
        define('get_posts', function(array $args){
            Assert::arrayNotHasKey('ignore_sticky_posts,', $args);
        });

        $orm->query()->fetch();
    }

    public function tearDown()
    {
        // undefine all the functions after each test to avoid test dependencies
        undefineAll();
    }
}

Installation

Use Composer to require the library in the project, (*2)

composer require --dev lucatume/function-mocker-le

Why mocking functions? What problem does this solve?

The library provides a lightweight and dependency-free function mocking solution.
The reason one might want to mock a function in the tests is to test any code that depends on a framework based on functions (e.g. WordPress).
The difference between this library and function-mocker is that this will only work when none of the function it defines are defined during the execution; function-mocker will allow instead monkey-patching the functions at runtime even if those are already defined.
Where function mocker depends on the Patchwork library to allow for user-land monkey-patching this library has a minimal code footprint and only provides a handful of functions.
Patchwork or function-mocker should be used if really loading the mocked functions source cannot be avoided in the tests., (*3)

Usage

The library core function is the define($function, $callback) one: it defines a function and sets its content to a callback; in its basic usage it allows setting up a mocked function return value in the tests:, (*4)

public function test_current_user_can(){
    tad\FunctionMockerLe\define('current_user_can', function(){
        return true;
    });

    $this->assertTrue(current_user_can('read_posts'));
}

The $callback argument can be a callable of any kind, in this example I'm using the Prophecy mocking engine to use set complex expectations:, (*5)

public function test_prophecy(){
    $pr = $this->prophesize(User::class);
    $pr->can('read_posts')->shouldBeCalled();
    $pr->can('edit_posts')->shouldNotBeCalled();

    tad\FunctionMockerLe\define('current_user_can', [$pr->reveal(), 'can']);

    current_user_can('read_posts');
    current_user_can('edit_posts'); // this will trigger a failure: not expected
    current_user_can('read_posts');
}

Relying on basic assertions provided by hte PhpUnit library is another option:, (*6)

public function test_prophecy(){
    tad\FunctionMockerLe\define('current_user_can', function($cap){
        PHPUnit\Framework\Assert::assertNotEquals('edit_posts', $cap);
    });

    current_user_can('read_posts');
    current_user_can('edit_posts'); // this will trigger a failure: not expected
    current_user_can('read_posts');
}

Where function-mocker tries to provide a feature-reach solution to the problem of mocking functions (and more); this project tries to provide just a basic starting point with no opinionated choices about its usage.
The other functions provided by the libary are just sugar wrappers around the define core; see the example and the /tests folder for clarity.
The undefine($function) and undefineAll() methods will "undefine" functions managed by the Function Mocker LE library; in this context "undefine" means that calling the undefined function will trhow an tad\FunctionMockerLe\UndefinedFunctionException., (*7)

Tests

To run the tests install the composer dependencies and run PHPUnit:, (*8)

composer install
vendor/bin/phpunit

The Versions

06/03 2018

dev-source-function-parsing

dev-source-function-parsing

A lightweight function mocking solution.

  Sources   Download

GPL-2.0

The Requires

 

The Development Requires

23/02 2018

dev-master

9999999-dev

A lightweight function mocking solution.

  Sources   Download

GPL-2.0

The Requires

  • php >=5.4

 

The Development Requires

02/09 2017

1.0.1

1.0.1.0

A lightweight function mocking solution.

  Sources   Download

GPL-2.0

The Requires

  • php >=5.4

 

The Development Requires

30/08 2017

1.0.0

1.0.0.0

A lightweight function mocking solution.

  Sources   Download

GPL-2.0

The Requires

  • php >=5.4

 

The Development Requires