mockery 模拟

composer地址:mockery/mockery - Packagist

github地址:地址

文档地址:Mockery — Mockery Docs 1.0-alpha documentation

根据文档介绍,mockery是php mock对象框架。根据js的mock框架的作用,估计mockery也是通过创建对象获取数值,所以以便用于测试。

一、安装

composer安装

composer require mockery/mockery

二、使用

翻译文档:php mockery 中文文档 · 看云

class Book {}

interface BookRepository {
    function find($id): Book;
    function findAll(): array;
    function add(Book $book): void;
}

$double = Mockery::mock(BookRepository::class);
$double->find(11);

#设置预期内容
#仅执行一次
$double->expects()->add($book);
#执行两次
$double->expects()->add($book)->twice();

#忽略验证预期内容
// $double = Mockery::mock()->shouldIgnoreMissing();
$double = Mockery::spy();

$double->foo(); // null
$double->bar(); // null

#全局辅助方法 全局方法存在则会覆盖
Mockery::globalHelpers();

$mock = mock(Some::class);
$spy = spy(Some::class);

#trait测试
trait Foo {
    function foo() {
        return $this->doFoo();
    }

    abstract function doFoo();
}

$double = Mockery::mock(Foo::class);
//新版
$double->allows()->doFoo()->andReturns(123);
//模拟方法 doFoo1 参数123 返回qwe
//allows 后直接设置方法名和参数
$double->allows()->doFoo1(123)->andReturns('qwe');
//旧版
$double->shouldReceive('doFoo1')->with(123)->andReturn('qwe');
$double->foo(); // int(123)
$double->doFoo1(123);//string(qwe)

源码实现原理

#mockery/mockery/library/Mockery/Mock.php
 public function mockery_init(\Mockery\Container $container = null, $partialObject = null, $instanceMock = true)
    {
        if (is_null($container)) {
            $container = new \Mockery\Container();
        }
        $this->_mockery_container = $container;
        if (!is_null($partialObject)) {
            //mockery_getMethods 调用
            $this->_mockery_partial = $partialObject;
        }

        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()) {
            foreach ($this->mockery_getMethods() as $method) {
                if ($method->isPublic()) {
                    $this->_mockery_mockableMethods[] = $method->getName();
                }
            }
        }

        $this->_mockery_instanceMock = $instanceMock;
    }
public function allows($something = [])
    {
        if (is_string($something)) {
            return $this->shouldReceive($something);
        }

        if (empty($something)) {
            return $this->shouldReceive();
        }

        foreach ($something as $method => $returnValue) {
            $this->shouldReceive($method)->andReturn($returnValue);
        }

        return $this;
    }
public function shouldReceive(...$methodNames)
    {
        ……
          $self->mockery_setExpectationsFor($method, $director);
        ……
    }
 public function __call($method, array $args)
    {
        return $this->_mockery_handleMethodCall($method, $args);
    }
protected function _mockery_handleMethodCall($method, array $args)
    {
        ……
        $rm = $this->mockery_getMethod($method);
        if ($rm && $rm->isProtected() && !$this->_mockery_allowMockingProtectedMethods) {
            ……
            return call_user_func_array(get_parent_class($this) . '::' . $method, $args);
        }
        $handler = $this->_mockery_findExpectedMethodHandler($method);
    }
//根据已存在的方法 调用
    public function mockery_getMethod($name)
    {
        foreach ($this->mockery_getMethods() as $method) {
            if ($method->getName() == $name) {
                return $method;
            }
        }

        return null;
    }
    protected function mockery_getMethods()
    {
        if (static::$_mockery_methods && \Mockery::getConfiguration()->reflectionCacheEnabled()) {
            return static::$_mockery_methods;
        }

        if (isset($this->_mockery_partial)) {
            $reflected = new \ReflectionObject($this->_mockery_partial);
        } else {
            $reflected = new \ReflectionClass($this);
        }

        return static::$_mockery_methods = $reflected->getMethods();
    }
//
    protected function _mockery_findExpectedMethodHandler($method)
    {
        if (isset($this->_mockery_expectations[$method])) {
            return $this->_mockery_expectations[$method];
        }

        $lowerCasedMockeryExpectations = array_change_key_case($this->_mockery_expectations, CASE_LOWER);
        $lowerCasedMethod = strtolower($method);

        if (isset($lowerCasedMockeryExpectations[$lowerCasedMethod])) {
            return $lowerCasedMockeryExpectations[$lowerCasedMethod];
        }

        return null;
    }
public function mockery_setExpectationsFor($method, \Mockery\ExpectationDirector $director)
    {
        $this->_mockery_expectations[$method] = $director;
    }

 $this->_mockery_partial 设置

#mockery/mockery/library/Mockery/Mock.php
public function mockery_init(\Mockery\Container $container = null, $partialObject = null, $instanceMock = true)
    {
        ……
        if (!is_null($partialObject)) {
            //mockery_getMethods 调用
            $this->_mockery_partial = $partialObject;
        }
        ……
    }

//设置targets  可以用多种方法调用 
//但具体实现位置 mockery/mockery/library/Mockery/Container.php
namespace Mockery
class Container{
 public function mock(...$args)
    {
        ……
        $builder = new MockConfigurationBuilder();
        ……
        $builder->addTargets($interfaces);//设置targets 
        ……
         $builder->addTarget('stdClass');
        ……
        $config = $builder->getMockConfiguration();
        ……
        $mock->mockery_init($this, $config->getTargetObject(), $config->isInstanceMock());
        ……
    }
}

#Mockery\Generator\MockConfigurationBuilder
    public function addTarget($target)
    {
        $this->targets[] = $target;

        return $this;
    }

    public function addTargets($targets)
    {
        foreach ($targets as $target) {
            $this->addTarget($target);
        }

        return $this;
    }
    public function getMockConfiguration()
    {
        return new MockConfiguration(
            $this->targets,//设置targets
            $this->blackListedMethods,
            $this->whiteListedMethods,
            $this->name,
            $this->instanceMock,
            $this->parameterOverrides,
            $this->mockOriginalDestructor,
            $this->constantsMap
        );
    }
    public function getTargetObject()
    {
        return $this->targetObject;
    }

#Mockery\Generator\MockConfiguration
 public function __construct(
        array $targets = array(),
        array $blackListedMethods = array(),
        array $whiteListedMethods = array(),
        $name = null,
        $instanceMock = false,
        array $parameterOverrides = array(),
        $mockOriginalDestructor = false,
        array $constantsMap = array()
    ) {
        $this->addTargets($targets);//设置targets
        $this->blackListedMethods = $blackListedMethods;
        $this->whiteListedMethods = $whiteListedMethods;
        $this->name = $name;
        $this->instanceMock = $instanceMock;
        $this->parameterOverrides = $parameterOverrides;
        $this->mockOriginalDestructor = $mockOriginalDestructor;
        $this->constantsMap = $constantsMap;
    }
protected function addTargets($interfaces)
    {
        foreach ($interfaces as $interface) {
            $this->addTarget($interface);
        }
    }
 protected function addTarget($target)
    {
         if (is_object($target)) {
            $this->setTargetObject($target);
            $this->setTargetClassName(get_class($target));
            return $this;
        }
        ……
    }
protected function setTargetObject($object)
    {
        $this->targetObject = $object;
    }
public function getTargetObject()
    {
        return $this->targetObject;
    }

 mock调用

#mockery/mockery/library/Mockery.php
if (!function_exists("mock")) {
    function mock(...$args)
    {
        return Mockery::mock(...$args);
    }


#mockery/mockery/library/helpers.php
namesapce Mockery
public static function mock(...$args)
    {
        return call_user_func_array(array(self::getContainer(), 'mock'), $args);
    }
}

#Mockery\Container
public function mock(...$args)
    {
        ……
    {

你可能感兴趣的:(php)