composer地址:mockery/mockery - Packagist
github地址:地址
文档地址:Mockery — Mockery Docs 1.0-alpha documentation
根据文档介绍,mockery是php mock对象框架。根据js的mock框架的作用,估计mockery也是通过创建对象获取数值,所以以便用于测试。
一、安装
composer安装
composer require mockery/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)
{
……
{