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中截取字符串支持utf-8
Jan 18 PHP
加速XP搜索功能堪比vista
Mar 22 PHP
配置Apache2.2+PHP5+CakePHP1.2+MySQL5运行环境
Apr 25 PHP
PHP 图片上传实现代码 带详细注释
Apr 29 PHP
PHP下对数组进行排序的函数
Aug 08 PHP
php中批量替换文件名的实现代码
Jul 20 PHP
PHP大小写问题:函数名和类名不区分,变量名区分
Jun 17 PHP
奉献出一个封装的curl函数 便于调用(抓数据专用)
Jul 22 PHP
php 微信公众平台开发模式实现多客服的实例代码
Nov 07 PHP
PHP的中使用非缓冲模式查询数据库的方法
Feb 05 PHP
PHP中ajax无刷新上传图片与图片下载功能
Feb 21 PHP
Yii2框架数据验证操作实例详解
May 02 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 echo,print,printf,sprintf函数之间的区别与用法详解
2013/11/27 PHP
PHP基于mssql扩展远程连接MSSQL的简单实现方法
2016/10/08 PHP
php自定义函数br2nl实现将html中br换行符转换为文本输入中换行符的方法【与函数nl2br功能相反】
2017/02/17 PHP
JavaScript高级程序设计 阅读笔记(十二) js内置对象Math
2012/08/14 Javascript
利用js正则表达式验证手机号,email地址,邮政编码
2014/01/23 Javascript
JavaScript中的typeof操作符用法实例
2014/04/05 Javascript
jQuery队列操作方法实例
2014/06/11 Javascript
浅谈javascript中的闭包
2015/05/13 Javascript
javascript实现确定和取消提示框效果
2015/07/10 Javascript
JS提交form表单实例分析
2015/12/10 Javascript
浅谈React碰到v-if
2018/11/04 Javascript
重学JS 系列:聊聊继承(推荐)
2019/04/11 Javascript
layer.open 获取不到表单信息的解决方法
2019/09/26 Javascript
layui清除radio的选中状态实例
2019/11/14 Javascript
JavaScript计算出两个数的差值
2020/03/19 Javascript
vue 数据操作相关总结
2020/12/17 Vue.js
python 获取et和excel的版本号
2009/04/09 Python
解决Python中由于logging模块误用导致的内存泄露
2015/04/23 Python
Python写的一个简单监控系统
2015/06/19 Python
使用Python编写基于DHT协议的BT资源爬虫
2016/03/19 Python
python去除文件中空格、Tab及回车的方法
2016/04/12 Python
通过python顺序修改文件名字的方法
2018/07/11 Python
python将字符串以utf-8格式保存在txt文件中的方法
2018/10/30 Python
python调用pyaudio使用麦克风录制wav声音文件的教程
2019/06/26 Python
解决django服务器重启端口被占用的问题
2019/07/26 Python
Python生成个性签名图片获取GUI过程解析
2019/12/16 Python
Python中的全局变量如何理解
2020/06/04 Python
Python爬虫之Selenium实现关闭浏览器
2020/12/04 Python
HTML5 manifest离线缓存的示例代码
2018/08/08 HTML / CSS
英国男士时尚购物网站:Stuarts London
2017/10/22 全球购物
印尼综合在线预订网站:Tiket.com(机票、酒店、火车、租车和娱乐)
2018/10/11 全球购物
sleep()方法和wait()方法的区别是什么
2012/11/17 面试题
军训拉歌口号
2014/06/13 职场文书
自荐信格式模板
2015/03/27 职场文书
2015年统计员个人工作总结
2015/07/23 职场文书
PostgreSQL逻辑复制解密原理解析
2022/09/23 PostgreSQL