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 相关文章推荐
第八节 访问方式 [8]
Oct 09 PHP
php 无限极分类
Mar 27 PHP
在字符串指定位置插入一段字符串的php代码
Feb 16 PHP
php 连接mssql数据库 初学php笔记
Mar 01 PHP
PHP反转字符串函数strrev()函数的用法
Feb 04 PHP
php实现单链表的实例代码
Mar 22 PHP
使用PHP生成PDF方法详解
Jan 23 PHP
PHP实现的通过参数生成MYSQL语句类完整实例
Apr 11 PHP
PHP5.5.15+Apache2.4.10+MySQL5.6.20配置方法分享
May 06 PHP
php结合ajax实现手机发红包的案例
Oct 13 PHP
基于ThinkPHP实现的日历功能实例详解
Apr 15 PHP
对php 判断http还是https,以及获得当前url的方法详解
Jan 15 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 while循环控制的简单实例
2016/05/30 PHP
yii2.0数据库迁移教程【多个数据库同时同步数据】
2016/10/08 PHP
php版微信返回用户text输入的方法
2016/11/14 PHP
微信第三方登录(原生)demo【必看篇】
2017/05/26 PHP
jquery的ajax从纯真网(cz88.net)获取IP地址对应地区名
2009/12/02 Javascript
Jquery下的26个实用小技巧(jQuery tips, tricks &amp; solutions)
2010/03/01 Javascript
jQuery队列控制方法详解queue()/dequeue()/clearQueue()
2010/12/02 Javascript
在Javascript里访问SharePoint列表数据的实现方法
2011/05/22 Javascript
jquery blockUI 遮罩不能消失与不能提交的解决方法
2011/09/17 Javascript
JavaScript中奇葩的假值示例应用
2014/03/11 Javascript
用Jquery选择器计算table中的某一列某一行的合计
2014/08/13 Javascript
JavaScript中的数组操作介绍
2014/12/30 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之绑定事件
2015/11/19 Javascript
jQuery插件ImageDrawer.js实现动态绘制图片动画(附源码下载)
2016/02/25 Javascript
JavaScript 中有关数组对象的方法(详解)
2016/08/15 Javascript
jQuery+pjax简单示例汇总
2017/04/21 jQuery
详解Vue2中组件间通信的解决全方案
2017/07/28 Javascript
利用Node.js了解与测量HTTP所花费的时间详解
2017/09/22 Javascript
实用的Vue开发技巧
2019/05/30 Javascript
Python Web框架Flask下网站开发入门实例
2015/02/08 Python
python使用socket向客户端发送数据的方法
2015/04/29 Python
利用Python自带PIL库扩展图片大小给图片加文字描述的方法示例
2017/08/08 Python
Python3.4实现从HTTP代理网站批量获取代理并筛选的方法示例
2017/09/26 Python
Django跨域请求原理及实现代码
2020/11/14 Python
python 利用matplotlib在3D空间绘制二次抛物面的案例
2021/02/06 Python
HTML5去掉输入框type为number时的上下箭头的实现方法
2020/01/03 HTML / CSS
AmazeUI 缩略图的实现示例
2020/08/18 HTML / CSS
Exoticca英国:以最优惠的价格提供豪华异国情调旅行
2018/10/18 全球购物
局部内部类是否可以访问非final变量?
2013/04/20 面试题
请解释在new与override的区别
2012/10/29 面试题
会计试用期自我评价怎么写
2014/09/18 职场文书
2014年社区党建工作汇报材料
2014/11/02 职场文书
离婚起诉书范本
2015/05/18 职场文书
元旦晚会主持词开场白
2015/05/28 职场文书
python Tkinter的简单入门教程
2021/04/11 Python
Win11如何启用启动修复 ? Win11执行启动修复的三种方法
2022/04/08 数码科技