PHPUnit测试私有属性和方法功能示例


Posted in PHP onJune 12, 2018

本文实例讲述了PHPUnit测试私有属性和方法功能。分享给大家供大家参考,具体如下:

一、测试类中的私有方法:

class Sample
{
  private $a = 0;
  private function run()
  {
    echo $a;
  }
}

上面只是简单的写了一个类包含,一个私有变量和一个私有方法。对于protected和private方法,由于无法像是用public方法一样直接调用,所以在使用phpunit进行单测的时候,多有不便,特别是当一个类中,对外只提供少量接口,内部使用了大量private方法的情况。

对于protected方法,建议使用继承的方式进行测试,在此就不再赘述。而对于private方法的测试,建议使用php的反射机制来进行。话不多说,上代码:

class testSample()
{
    $method = new ReflectionMethod('Sample', 'run');
    $method->setAccessible(true); //将run方法从private变成类似于public的权限
    $method->invoke(new Sample()); //调用run方法
}

如果run方法是静态的,如:

private static function run()
{
  echo 'run is a private static function';
}

那么invoke函数还可以这么写:

$method->invoke(null); //只有静态方法可以不必传类的实例化

如果run还需要传参,比如:

private function run($x, $y)
{
  return $x + $y;
}

那么,测试代码可以改为:

$method->invokeArgs(new Sample(), array(1, 2));
//array中依次写入要传的参数。执行结果返回3

【注意】:利用反射的方法测试私有方法虽好,但setAccessible函数是php5.3.2版本以后才支持的(>=5.3.2)

二、私有属性的get/set

说完了私有方法,再来看看私有属性,依旧拿Sample类作为例子,想要获取或设置Sample类中的私有属性$a的值可以用如下方法:

public function testPrivateProperty()
{
  $reflectedClass = new ReflectionClass('Sample');
  $reflectedProperty = $reflectedClass->getProperty('a');
  $reflectedProperty->setAccessible(true);
  $reflectedProperty->getValue(); //获取$a的值
  $reflectedProperty->setValue(123); //给$a赋值:$a = 123;
}

上述方法对静态属性依然有效。

到此,是不是瞬间感觉测试私有方法或属性变得很容易了。

附:PHPunit 测试私有方法(英文原文)

This article is part of a series on testing untestable code:

  • Testing private methods
  • Testing code that uses singletons
  • Stubbing static methods
  • Stubbing hard-coded dependencies

No, not those privates. If you need help with those, this book might help.

One question I get over and over again when talking about Unit Testing is this:

"How do I test the private attributes and methods of my objects?"

Lets assume we have a class Foo:

<?php
class Foo
{
  private $bar = 'baz';
  public function doSomething()
  {
    return $this->bar = $this->doSomethingPrivate();
  }
  private function doSomethingPrivate()
  {
    return 'blah';
  }
}
?>

Before we explore how protected and private attributes and methods can be tested directly, lets have a look at how they can be tested indirectly.

The following test calls the testDoSomething() method which in turn calls thedoSomethingPrivate() method:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
  /**
   * @covers Foo::doSomething
   * @covers Foo::doSomethingPrivate
   */
  public function testDoSomething()
  {
    $foo = new Foo;
    $this->assertEquals('blah', $foo->doSomething());
  }
}
?>

The test above assumes that testDoSomething() only works correctly whentestDoSomethingPrivate() works correctly. This means that we have indirectly testedtestDoSomethingPrivate(). The problem with this approach is that when the test fails we do not know directly where the root cause for the failure is. It could be in eithertestDoSomething() or testDoSomethingPrivate(). This makes the test less valuable.

PHPUnit supports reading protected and private attributes through thePHPUnit_Framework_Assert::readAttribute() method. Convenience wrappers such asPHPUnit_Framework_TestCase::assertAttributeEquals() exist to express assertions onprotected and private attributes:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
  public function testPrivateAttribute()
  {
    $this->assertAttributeEquals(
     'baz', /* expected value */
     'bar', /* attribute name */
     new Foo /* object     */
    );
  }
}
?>

PHP 5.3.2 introduces the ReflectionMethod::setAccessible() method to allow the invocation of protected and private methods through the Reflection API:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
  /**
   * @covers Foo::doSomethingPrivate
   */
  public function testPrivateMethod()
  {
    $method = new ReflectionMethod(
     'Foo', 'doSomethingPrivate'
    );
    $method->setAccessible(TRUE);
    $this->assertEquals(
     'blah', $method->invoke(new Foo)
    );
  }
}
?>

In the test above we directly test testDoSomethingPrivate(). When it fails we immediately know where to look for the root cause.

I agree with Dave Thomas and Andy Hunt, who write in their book "Pragmatic Unit Testing":

"In general, you don't want to break any encapsulation for the sake of testing (or as Mom used to say, "don't expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out."

So: Just because the testing of protected and private attributes and methods is possible does not mean that this is a "good thing".

参考文献:

http://php.net/manual/en/class.reflectionmethod.php

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
基于php split()函数的用法详解
Jun 05 PHP
PHP实现多图片上传类实例
Jul 26 PHP
php mb_substr()函数截取中文字符串应用示例
Jul 29 PHP
PHP的伪随机数与真随机数详解
May 27 PHP
PHP安装memcached扩展笔记
May 28 PHP
PHP实现XML与数据格式进行转换类实例
Jul 29 PHP
Yii框架表单提交验证功能分析
Jan 07 PHP
php pdo操作数据库示例
Mar 10 PHP
PHP实现的简单异常处理类示例
May 04 PHP
php封装db类连接sqlite3数据库的方法实例
Dec 19 PHP
PHP实现通过二维数组键值获取一维键名操作示例
Oct 11 PHP
Thinkphp 框架扩展之标签库驱动原理与用法分析
Apr 23 PHP
PHP+redis实现的悲观锁机制示例
Jun 12 #PHP
thinkPHP5框架auth权限控制类与用法示例
Jun 12 #PHP
thinkPHP5框架实现基于ajax的分页功能示例
Jun 12 #PHP
Laravel框架路由和控制器的绑定操作方法
Jun 12 #PHP
Laravel框架路由设置与使用示例
Jun 12 #PHP
Laravel框架生命周期与原理分析
Jun 12 #PHP
Laravel框架分页实现方法分析
Jun 12 #PHP
You might like
php制作中间带自己定义图片二维码的方法
2014/01/27 PHP
PHP提示Warning:phpinfo() has been disabled函数禁用的解决方法
2014/12/17 PHP
php结合安卓客户端实现查询交互实例
2015/05/05 PHP
PHP基于接口技术实现简单的多态应用完整实例
2017/04/26 PHP
js获取dom的高度和宽度(可见区域及部分等等)
2013/06/13 Javascript
jquery 实现窗口的最大化不论什么情况
2013/09/03 Javascript
js动态添加删除,后台取数据(示例代码)
2013/11/25 Javascript
javascript遍历控件实例详细解析
2014/01/10 Javascript
基于Jquery easyui 选中特定的tab
2015/11/17 Javascript
JavaScript Math 对象常用方法总结
2016/04/28 Javascript
JS获取中文拼音首字母并通过拼音首字母快速查找页面内对应中文内容的方法【附demo源码】
2016/08/19 Javascript
简单实现bootstrap选项卡效果
2017/02/08 Javascript
QRCode.js:基于JQuery的生成二维码JS库的使用
2017/06/23 jQuery
IntelliJ IDEA 安装vue开发插件的方法
2017/11/21 Javascript
[46:48]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第三局
2016/02/25 DOTA
[01:09]DOTAPLUS——DOTA2的新时代
2018/04/04 DOTA
python 实现一个贴吧图片爬虫的示例
2017/10/12 Python
Django实现快速分页的方法实例
2017/10/22 Python
python Django的web开发实例(入门)
2019/07/31 Python
在python中计算ssim的方法(与Matlab结果一致)
2019/12/19 Python
Pytorch在dataloader类中设置shuffle的随机数种子方式
2020/01/14 Python
利用python绘制数据曲线图的实现
2020/04/09 Python
python 负数取模运算实例
2020/06/03 Python
Python plt 利用subplot 实现在一张画布同时画多张图
2021/02/26 Python
pandas数据分组groupby()和统计函数agg()的使用
2021/03/04 Python
通信工程专业个人找工作求职信范文
2013/09/21 职场文书
工商管理系学生的自我评价分享
2013/11/29 职场文书
小学国庆节活动方案
2014/02/11 职场文书
艺术设计专业求职自荐信
2014/05/19 职场文书
学校学雷锋活动总结
2014/06/26 职场文书
入学证明
2015/06/23 职场文书
Pygame如何使用精灵和碰撞检测
2021/11/17 Python
Go获取两个时区的时间差
2022/04/20 Golang
git中cherry-pick命令的使用教程
2022/06/25 Servers
Pytorch中expand()的使用(扩展某个维度)
2022/07/15 Python
java获取一个文本文件的编码(格式)信息
2022/09/23 Java/Android