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中ob(Output Buffer 输出缓冲)函数使用方法
Jul 21 PHP
一些被忽视的PHP函数(简单整理)
Apr 30 PHP
PHP array 的加法操作代码
Jul 24 PHP
phpexcel导出excel的颜色和网页中的颜色显示不一致
Dec 11 PHP
php 删除目录下N分钟前创建的所有文件的实现代码
Aug 10 PHP
php中require和require_once的区别说明
Feb 27 PHP
PHP 之 写时复制介绍(Copy On Write)
May 13 PHP
php返回当前日期或者指定日期是周几
May 21 PHP
浅析Yii2 GridView 日期格式化并实现日期可搜索教程
Apr 22 PHP
PHP PDO操作MySQL基础教程
Jun 05 PHP
浅析PHP 中move_uploaded_file 上传中文文件名失败
Apr 17 PHP
Yii框架使用PHPExcel导出Excel文件的方法分析【改进版】
Jul 24 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
LotusPhp笔记之:Cookie组件的使用详解
2013/05/06 PHP
深入mysql_fetch_row()与mysql_fetch_array()的区别详解
2013/06/05 PHP
ThinkPHP的cookie和session冲突造成Cookie不能使用的解决方法
2014/07/01 PHP
ThinkPHP实现转换数据库查询结果数据到对应类型的方法
2017/11/16 PHP
javascript英文日期(有时间)选择器
2007/05/02 Javascript
List the Stored Procedures in a SQL Server database
2007/06/20 Javascript
Jquery中&quot;$(document).ready(function(){ })&quot;函数的使用详解
2013/12/30 Javascript
JQuery设置获取下拉菜单某个选项的值(比较全)
2014/08/05 Javascript
JS正则表达式修饰符global(/g)用法分析
2016/12/27 Javascript
微信小程序开发之入门实例教程篇
2017/03/07 Javascript
js仿拉勾网首页穿墙广告效果
2017/03/08 Javascript
jQuery插件FusionCharts绘制的2D帕累托图效果示例【附demo源码】
2017/03/28 jQuery
js实现拖拽上传图片功能
2017/08/01 Javascript
工作中常用到的ES6语法
2018/09/04 Javascript
JS module的导出和导入的实现代码
2019/02/25 Javascript
vue实现配置全局访问路径头(axios)
2019/11/01 Javascript
python 时间戳与格式化时间的转化实现代码
2016/03/23 Python
Python正则表达式和元字符详解
2018/11/29 Python
Django的CVB实例详解
2020/02/10 Python
Python利用逻辑回归分类实现模板
2020/02/15 Python
自定义Django Form中choicefield下拉菜单选取数据库内容实例
2020/03/13 Python
HTML5本地存储localStorage、sessionStorage基本用法、遍历操作、异常处理等
2014/05/08 HTML / CSS
美国大尺码女装零售商:TORRID
2016/10/01 全球购物
什么是makefile? 如何编写makefile?
2012/08/08 面试题
大学四年的个人自我评价
2014/01/14 职场文书
邮政员工辞职信
2014/01/16 职场文书
珍惜水资源建议书
2014/03/12 职场文书
学校学习雷锋活动总结
2014/07/03 职场文书
预备党员自我批评思想汇报
2014/10/10 职场文书
赔偿协议书
2015/01/27 职场文书
2015年服务员工作总结
2015/04/08 职场文书
证券区域经理岗位职责
2015/04/10 职场文书
大学生学习十八届五中全会精神心得体会
2016/01/05 职场文书
2019请假条的基本格式及范文!
2019/07/05 职场文书
Python如何利用正则表达式爬取网页信息及图片
2021/04/17 Python
windows安装python超详细图文教程
2021/05/21 Python