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 相关文章推荐
PHP4与PHP5的时间格式问题
Feb 17 PHP
php单例模式实现(对象只被创建一次)
Dec 05 PHP
php实现utf-8和GB2312编码相互转换函数代码
Feb 07 PHP
php实例分享之mysql数据备份
May 19 PHP
Yii框架调试心得--在页面输出执行sql语句
Dec 25 PHP
PHP、Python和Javascript的装饰器模式对比
Feb 03 PHP
php结合正则获取字符串中数字
Jun 19 PHP
PHP加密解密函数详解
Oct 28 PHP
PHP的Yii框架中过滤器相关的使用总结
Mar 29 PHP
PHP常用操作类之通信数据封装类的实现
Jul 16 PHP
PHP中的empty、isset、isnull的区别与使用实例
Mar 22 PHP
使用tp框架和SQL语句查询数据表中的某字段包含某值
Oct 18 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
Mysql的常用命令
2006/10/09 PHP
实用函数8
2007/11/08 PHP
PHP将两个关联数组合并函数提高函数效率
2014/03/18 PHP
Zend Framework实现多文件上传功能实例
2016/03/21 PHP
PHP递归遍历指定文件夹内的文件实现方法
2016/11/15 PHP
Javascript的IE和Firefox兼容性汇编(zz)
2007/02/02 Javascript
用JQuery实现全选与取消的两种简单方法
2014/02/22 Javascript
javaScript事件机制兼容【详细整理】
2016/07/23 Javascript
浅谈jQuery中的checkbox问题
2016/08/10 Javascript
KnockoutJS 3.X API 第四章之表单submit、enable、disable绑定
2016/10/10 Javascript
Angular的$http的ajax的请求操作(推荐)
2017/01/10 Javascript
AngularJS实现路由实例
2017/02/12 Javascript
基于Node的React图片上传组件实现实例代码
2017/05/10 Javascript
IScroll5实现下拉刷新上拉加载的功能实例
2017/08/11 Javascript
使用 Node.js 模拟滑动拼图验证码操作的示例代码
2017/11/02 Javascript
浅谈react受控组件与非受控组件(小结)
2018/02/09 Javascript
vuejs实现标签选项卡动态更改css样式的方法
2018/05/31 Javascript
Vue项目中最新用到的一些实用小技巧
2018/11/06 Javascript
解决layui-table单元格设置为百分比在ie8下不能自适应的问题
2019/09/28 Javascript
JavaScript cookie原理及使用实例
2020/05/08 Javascript
详解Python中open()函数指定文件打开方式的用法
2016/06/04 Python
解决python2.7 查询mysql时出现中文乱码
2016/10/09 Python
python模拟事件触发机制详解
2018/01/19 Python
Python学习笔记之图片人脸检测识别实例教程
2019/03/06 Python
Python利用scapy实现ARP欺骗的方法
2019/07/23 Python
Python函数中的可变长参数详解
2019/09/12 Python
Python MongoDB 插入数据时已存在则不执行,不存在则插入的解决方法
2019/09/24 Python
python 读取.nii格式图像实例
2020/07/01 Python
Python爬虫之Selenium鼠标事件的实现
2020/12/04 Python
美国最大的香水连锁店官网:Perfumania
2016/08/15 全球购物
介绍一下linux的文件系统
2012/03/20 面试题
优秀员工自荐书范文
2013/12/08 职场文书
建筑专业毕业生自荐信
2014/05/25 职场文书
财产分割协议书
2016/03/22 职场文书
apache基于端口创建虚拟主机的示例
2021/04/22 Servers
解析探秘fescar分布式事务实现原理
2022/02/28 Java/Android