Code Monkey home page Code Monkey logo

moka's Introduction

Moka - Shorthand for Creating Mock Objects

Packagist GitHub release Travis Scrutinizer Coverage Scrutinizer Packagist

Tired of spending most of your testing time mocking objects like there's no tomorrow? Yes.
Moka provides you with two simple methods to reduce your effort on such a tedious task, and with an incredible abstraction layer between the most popular mock engines and you.

Installation

You can install the package via composer:

composer require --dev facile-it/moka

Usage

To use Moka in your tests simply use function Moka\Plugin\PHPUnit\moka() (see generators section below) and run Moka::clean() before every test. A simple interface will let you create moka (mock) objects and decorate them with stub methods and properties via a fluent interface:

<?php

namespace Foo\Tests;

use Moka\Moka;
use function Moka\Plugin\PHPUnit\moka;

class FooTest extends \AnyTestCase
{
    private $foo;
    
    protected function setUp(): void
    {
        Moka::clean();
        
        // The subject of the test.
        $this->foo = new Foo(
            moka(BarInterface::class)->stub([
                // Property name => value.
                '$property' => 3,
                // Method name => return value.
                'method1' => moka(AcmeInterface::class),
                'method2' => true
            ])
        );
    }
    
    //...
}

Alternatively, instead of using moka(), you can call Moka::phpunit(string $fqcnOrAlias, string $alias = null): ProxyInterface.

Being such a simple project, Moka can be integrated in an already existing test suite with no effort.

Notice: if you are extending PHPUnit TestCase, to simplify the cleaning phase we provide a MokaCleanerTrait which automatically runs Moka::clean() after each test.

<?php

namespace Foo\Tests;

use Moka\Traits\MokaCleanerTrait;
use PHPUnit\Framework\TestCase;
use function Moka\Plugin\PHPUnit\moka;

class FooTest extends TestCase
{
    use MokaCleanerTrait;
    
    protected function setUp(): void
    {
        // No call to Moka::clean() needed.
        
        // ...
    }
    
    // ...
}

You can rely on the original mock object implementation to be accessible (in the example below, PHPUnit's - for Prophecy see below):

<?php

moka(BarInterface::class, 'bar')
    ->expects($this->at(0))
    ->method('isValid')
    ->willReturn(true);

moka('bar')
    ->expects($this->at(1))
    ->method('isValid')
    ->willThrowException(new \Exception());

var_dump(moka('bar')->isValid());
// bool(true)

var_dump(moka('bar')->isValid());
// throws \Exception

Reference

moka(string $fqcnOrAlias, string $alias = null): ProxyInterface

Creates a proxy containing a mock object (according to the selected strategy) for the provided FQCN and optionally assigns an $alias to it to be able to get it later:

<?php

$mock1 = moka(FooInterface::class); // Creates the mock for FooInterface.
$mock2 = moka(FooInterface::class); // Gets a different mock.

var_dump($mock1 === $mock2);
// bool(false)

The $alias allows you to store mock instances:

<?php

$mock1 = moka(FooInterface::class, 'foo'); // Creates a mock for FooInterface.
$mock2 = moka('foo'); // Get the mock previously created.

var_dump($mock1 === $mock2);
// bool(true)

ProxyInterface::stub(array $namesWithValues): ProxyInterface

Accepts an array of method or property stubs with format [$name => $value], where $name must be a string and $value can be of any type, including another mock object.

Caution:

  • Properties are identified by symbol $ prepended to their names
  • An exception instance set as a method value will be thrown when the method is called
<?php

$mock = moka(BarInterface::class)->stub([
    '$property' => 1,
    'isValid' => true,
    'getMock' => moka(AcmeInterface::class),
    'throwException' => new \Exception()
]);

var_dump($mock->property);
// int(1)

var_dump($mock->isValid());
// bool(true)

Notice: method stubs are valid for any invocation of the defined methods and cannot be overridden.
If you need more granular control over invocation strategies, you can get access to the original mock object implementation.

Supported mock object generators

Currently we ship Moka with built-in support for PHPUnit mock objects.
We support other generators as well, but you need to install the relevant packages to make them work:

We provide a specific moka() function for each supported strategy, as well as a static method (self documented in the function itself):

  • Moka\Plugin\PHPUnit\moka
  • Moka\Plugin\Prophecy\moka
  • Moka\Plugin\Mockery\moka
  • Moka\Plugin\Phake\moka

Prophecy native behavior

Prophecy lets you stub methods by calling them directly on the ObjectProphecy. Moka doesn't support such a behavior, but we provide an easy workaround:

<?php

// Native Prophecy behavior...
$this->prophesize(FooInterface::class)
    ->someMethod(new AnyValuesToken())
    ->willReturn($something);

// ...translates to...
Moka::prophecy(FooInterface::class)
    ->someMethod->set(new AnyValuesToken())
    ->willReturn($something);

Warning: this workaround cannot be used with methods having the same name as a previously stubbed property:

<?php

Moka::prophecy(FooInterface::class, 'foo')->stub([
    '$someName' => true
]);

var_dump(Moka::prophecy('foo')->someName);
// bool(true)

Moka::prophecy('foo')
    ->someName->set(new AnyValuesToken())
    ->willReturn($something);
// throws \Exception

Plugin development

If you feel a genius and want to create your own mock generator (or add support for an existing one), just implement Moka\Plugin\PluginInterface and the relative Moka\Strategy\MockingStrategyInterface:

<?php

namespace Moka\Plugin\YourOwn;

use Moka\Plugin\PluginInterface;
use Moka\Strategy\MockingStrategyInterface;

class YourOwnPlugin implements PluginInterface
{
    public static function getStrategy(): MockingStrategyInterface 
    {
        return new YourOwnMockingStrategy();
    }
}

Extend AbstractMockingStrategy for an easier (and stricter) implementation of your strategy:

<?php

namespace Moka\Plugin\YourOwn;

use Moka\Strategy\AbstractMockingStrategy;
use Moka\Stub\MethodStub;

class YourOwnMockingStrategy extends AbstractMockingStrategy
{
    public function __construct()
    {
        // TODO: Implement __construct() method.
    }
    
    protected function doBuild(string $fqcn)
    {
        // TODO: Implement doBuild() method.
    }
    
    protected function doDecorateWithMethod($mock, MethodStub $stub)
    {
        // TODO: Implement doDecorateWithMethod() method.
    }
    
    protected function doGet($mock)
    {
        // TODO: Implement doGet() method.
    }

    protected function doCall($mock, string $methodName)
    {
        // Override doCall() if you need special behavior.
        // See ProphecyMockingStrategy::doCall().
    }
}

Warning: your plugin FQCN must match the template Moka\Plugin\YourOwn\YourOwnPlugin, where YourOwn is the name of the plugin.
Both your plugin and your strategy must pass our test cases (please install phpunit/phpunit to run them):

  • MokaPluginTestCase
  • MokaMockingStrategyTestCase

Let us know of any Moka-related development!

Testing

We highly suggest using Paraunit for a faster execution of tests:

composer global require facile-it/paraunit

paraunit run

Credits

License

The MIT License (MIT). Please see License File for more information.

moka's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

moka's Issues

ProxyTrait::stub() actually return $this.

Is not a good design if we want to compose functionality for Proxies (ProxyInterface) because we can't warranted the return type.

Refactor of this part or include this method in an abstract proxy.

    /**
     * @param array $namesWithValues
     * @return ProxyInterface
     *
     * @throws InvalidArgumentException
     * @throws MockNotCreatedException
     */
    public function stub(array $namesWithValues): ProxyInterface
    {
        //...

        /** @var $this ProxyInterface */
        $this->__moka_mockingStrategy->decorate($this->__moka_mock, $namesWithValues);

        return $this;
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.