测试替身

本文详细介绍了在PHPUnit中如何使用测试替身,包括桩件(Stubs)的创建和设置返回值,仿件对象(Mock Objects)的使用,以及Prophecy、对trait和abstract类的模拟、对web服务和文件系统的模拟。通过这些技术,可以在测试中避免真实对象的依赖,确保测试的独立性和准确性。
摘要由CSDN通过智能技术生成

9 测试替身

PHPUnit 提供的 createMock($type)getMockBuilder($type) 方法可以在测试中用来自动生成对象,此对象可以充当任意指定原版类型(接口或类名)的测试替身。在任何预期或要求使用原版类的实例对象的上下文中都可以使用这个测试替身对象来代替。

createMock($type) 方法直接返回指定类型(接口或类)的测试替身对象实例。此测试替身的创建使用了最佳实践的默认值(不执行原始类的 __construct()__clone() 方法,且不对传递给测试替身的方法的参数进行克隆)。如果这些默认值非你所需,可以用 getMockBuilder($type) 方法并使用流畅式接口来定制测试替身的生成过程。

在默认情况下,原版类的所有方法都会被替换为只会返回 null 的伪实现(其中不会调用原版方法)。使用诸如 will($this->returnValue()) 之类的方法可以对这些伪实现在被调用时应当返回什么值做出配置。

简单来说就是一些测试类需要调用其他的类作为参数,但是我们又不想创建那些类,所以PHPUnit提供了一种模仿机制。

桩件:Stubs

将对象替换成配置好对应返回值的替身的方法成为上桩(stubbing)

设置返回值

可以返回的数据种类有:

  • 固定值
  • 传入的参数
  • 返回桩件类自身
  • 根据传入的参数,返回不同的值
  • 设置回调函数,计算返回值
  • 设置同一方法的多次调用时,每次的返回值
  • 设置抛出指定异常

下面是demo

require_once __DIR__."/../../vendor/autoload.php";

use PHPUnit\Framework\TestCase;

class SomeClass
{
   
    public function doSomething()
    {
   
        // 随便做点什么。
        return "data";
    }
    public function callUndefinedFunction($param){
   
        return "你不可以调用在原始类中未定义的方法";
    }
    public function classTest(){
   
        return "self";
    }
    public function returnValueByParam(){
   

    }
    public function returnDataByCallBack(){
   

    }
    public function returnDataByList(){
   

    }
    public function throwExceptions(){
   

    }
}

class Mock extends TestCase{
   
    public function testExample(){
   

        $stub = $this->createMock(SomeClass::class);
        // 返回固定值
        $stub->method('doSomething')
            ->willReturn('foo');
        // 返回参数
        $stub->method("callUndefinedFunction")
            ->will($this->returnArgument(0));
        // 返回该桩类自身
        $stub->method("classTest")
            ->will($this->returnSelf());
        // 根据不同参数,返回不同的值
        $map=array(
            // 最后一个值永远是返回的结果,而前面的就是传递的参数
            array("param1","param2","param3",'result1'),
            array('param2','param3','result2'),
            array("result3")
        );
        $stub->method("returnValueByParam")
            ->will($this->returnValueMap($map));
        // 下面调用时传入多少个参数,都会自动传入这里。
        // 但是,下面调用的参数只能比这里多,不能少,否则会报错误
        $callBackFunction=function ($param1,$param2,$param3){
   
            print_r(array(
                $param1,
                $param2,
                $param3
            ));
        };
        $stub->method("returnDataByCallBack")
            ->will($this->returnCallback($callBackFunction));
        // 设置每一次调用的返回值
        $stub->method("returnDataByList")
            ->will($this->onConsecutiveCalls(1,2,3,4,5,6));// 注意,这里传入的不是数组!
        // 设置代码抛出异常
        $stub->method("throwExceptions")
            ->will($this->throwException(new \Exception("没事情,就是抛异常玩玩")));
        $this->assertEquals('foo', $stub->doSomething());
        $this->assertEquals("hello",$stub->callUndefinedFunction("hello"));
        $this->assertSame($stub,$stub->classTest());
        $this->assertEquals("result1",$stub->returnValueByParam("param1","param2","param3"));
        $this->assertEquals("result2",$stub->returnValueByParam('param2','param3'));
        $this->assertEquals("result3",$stub->returnValueByParam());
        // 这里调用上面设置的方法,其中的参数会自动传入回调函数中
//        $stub->returnDataByCallBack(1,2,3,4);
        $this->assertEquals(1,$stub->returnDataByList());
        $this->assertEquals(2,$stub->returnDataByList());
        try{
   
            $stub->throwExceptions();
        }catch (Exception $e){
   
            print "\r\n".$e->getMessage()."\r\n";
        }
    }
}

注意,不能使用$stub->method()设置对应类中不存在的方法,否则会报错。

创建桩件时进行限制

public function testStubSetting(){
   
        $stub=$this->getMockBuilder(SomeClassSecond::class)
                    ->disableOriginalClone()// 开启设置之后,原类的 __clone 函数就不会运行了
                    ->disableOriginalConstructor()// 开启这个之后,就不会运行原类的 __construct 方法了
                    ->disableArgumentCloning()// 开启这个时候就不会在创建桩件的时候,复制原类的属性
                    ->disallowMockingUnknownTypes()// 不允许定义不存在的属性
                    ->getMock();
        $stub2= clone $stub;// 即使上面限制了 disableOriginalClone,但是那只是限制不能运行原类的 __clone,clone函数还是生效的,所以下面的断言是正确的
        $this->assertEquals($stub2,$stub,"两个类不一致");
    }

仿件对象:Mock Object

仿件对象有桩件的功能,但是比桩件更加贴合原来的类。举个例子就是桩件只能调用设置好的方法,不能调用原类的一些方法,但是仿件对象可以。

创建仿件对象

<?php
require_once __DIR__."/../../vendor/autoload.php";

use PHPUnit\Framework\TestCase;

class Subject
{
   
    protected $observers = [];
    protected $name;

    public function __construct($name)
    {
   
        $this->name = $name;
    }

    public function getName()
    {
   
        return $this->name;
    }

    public function attach(Observer $observer)
    {
   
        $this->observers[] = $observer;
    }

    public function doSomething()
    {
   
        // 做点什么
        // ...

        // 通知观察者发生了些什么
        $this->notify('something');
    }

    public function doSomethingBad</
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值