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连mysql和oracle数据库性能比较
Oct 09 PHP
BBS(php &amp; mysql)完整版(四)
Oct 09 PHP
php 运行效率总结(提示程序速度)
Nov 26 PHP
PHP原理之异常机制深入分析
Aug 08 PHP
解析PHP实现多进程并行执行脚本
Jun 18 PHP
使用php验证复选框有效性的示例
Nov 13 PHP
php获取当前时间的毫秒数的方法
Jan 26 PHP
PHP APC配置文件2套和参数详解
Jun 11 PHP
PHP中对各种加密算法、Hash算法的速度测试对比代码
Jul 08 PHP
使用PHP Socket 编程模拟Http post和get请求
Nov 25 PHP
php进程间通讯实例分析
Jul 11 PHP
php实现的PDO异常处理操作分析
Dec 27 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数据库连接时容易出错的特殊符号问题
2010/09/01 PHP
php打造智能化的柱状图程序,用于报表等
2015/06/19 PHP
百万级别知乎用户数据抓取与分析之PHP开发
2015/09/28 PHP
Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例
2020/04/04 PHP
实现超用户体验 table排序javascript实现代码
2009/06/22 Javascript
网页运行时提示对象不支持abigimage属性或方法
2014/08/10 Javascript
AngularJS中实现动画效果的方法
2016/07/28 Javascript
js仿腾讯QQ的web登陆界面
2016/08/19 Javascript
JavaScript用构造函数如何获取变量的类型名
2016/12/23 Javascript
VUE长按事件需求详解
2017/10/18 Javascript
Vue项目全局配置页面缓存之按需读取缓存的实现详解
2018/08/01 Javascript
重学JS 系列:聊聊继承(推荐)
2019/04/11 Javascript
ES6的异步操作之promise用法和async函数的具体使用
2019/12/06 Javascript
js简单实现自动生成表格功能示例
2020/06/02 Javascript
js中复选框的取值及赋值示例详解
2020/10/18 Javascript
JavaScript手写数组的常用函数总结
2020/11/22 Javascript
vue3.0中友好使用antdv示例详解
2021/01/05 Vue.js
python中cPickle用法例子分享
2014/01/03 Python
Django中的“惰性翻译”方法的相关使用
2015/07/27 Python
python3实现磁盘空间监控
2018/06/21 Python
Tensorflow中的placeholder和feed_dict的使用
2018/07/09 Python
python爬取哈尔滨天气信息
2018/07/14 Python
使用python验证代理ip是否可用的实现方法
2018/07/25 Python
使用EduBlock轻松学习Python编程
2018/10/08 Python
Python3标准库总结
2019/02/19 Python
python双端队列原理、实现与使用方法分析
2019/11/27 Python
Python获取二维数组的行列数的2种方法
2020/02/11 Python
python_array[0][0]与array[0,0]的区别详解
2020/02/18 Python
python selenium自动化测试框架搭建的方法步骤
2020/06/14 Python
python 实现图片修复(可用于去水印)
2020/11/19 Python
Django filter动态过滤与排序实现过程解析
2020/11/26 Python
优秀实习自我鉴定
2013/12/04 职场文书
秋季开学典礼主持词
2014/03/19 职场文书
化学工程专业求职信
2014/08/10 职场文书
交通事故和解协议书
2014/09/25 职场文书
财务负责人岗位职责
2015/02/03 职场文书