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作的文本留言本的例子(五)
Oct 09 PHP
生成静态页面的PHP类
Nov 25 PHP
PHP 中英文混合排版中处理字符串常用的函数
Apr 12 PHP
php循环输出数据库内容的代码
May 24 PHP
探讨:如何使用PHP实现计算两个日期间隔的年、月、周、日数
Jun 13 PHP
PHP语法自动检查的Vim插件
Aug 11 PHP
Netbeans 8.2与PHP相关的新特性介绍
Oct 08 PHP
php封装的数据库函数与用法示例【参考thinkPHP】
Nov 08 PHP
thinkPHP中_initialize方法实例分析
Dec 05 PHP
php 中的closure用法详解
Jun 12 PHP
实例讲解PHP验证邮箱是否合格
Jan 28 PHP
ThinkPHP5分页paginate代码实例解析
Nov 10 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中str_replace函数使用小结
2008/10/11 PHP
php一行代码获取文件后缀名实例分析
2014/11/12 PHP
PHP递归遍历指定目录的文件并统计文件数量的方法
2015/03/24 PHP
PHP使用in_array函数检查数组中是否存在某个值
2015/03/25 PHP
php smtp实现发送邮件功能
2017/06/22 PHP
PHP基于SimpleXML生成和解析xml的方法示例
2017/07/17 PHP
laravel邮件发送的实现代码示例
2020/01/31 PHP
window.onload 加载完毕的问题及解决方案(上)
2009/07/09 Javascript
javascript实现图片切换的幻灯片效果源代码
2012/12/12 Javascript
Jquery Easyui搜索框组件SearchBox使用详解(19)
2016/12/17 Javascript
jquery radio 动态控制选中失效问题的解决方法
2018/02/28 jQuery
vue2.0项目实现路由跳转的方法详解
2018/06/21 Javascript
vue仿element实现分页器效果
2018/09/13 Javascript
JS实现的tab页切换效果完整示例
2018/12/18 Javascript
bootstrap Table实现合并相同行
2019/07/19 Javascript
微信小程序下拉加载和上拉刷新两种实现方法详解
2019/09/05 Javascript
[59:08]Ti4 冒泡赛第二天 NEWBEE vs Titan 2
2014/07/15 DOTA
初学Python实用技巧两则
2014/08/29 Python
python实现web方式logview的方法
2015/08/10 Python
使用Python实现BT种子和磁力链接的相互转换
2015/11/09 Python
Python3实现的爬虫爬取数据并存入mysql数据库操作示例
2018/06/06 Python
详解Appium+Python之生成html测试报告
2019/01/04 Python
详解Python装饰器
2019/03/25 Python
django框架基于模板 生成 excel(xls) 文件操作示例
2019/06/19 Python
pycharm如何实现跨目录调用文件
2020/02/28 Python
Django 允许局域网中的机器访问你的主机操作
2020/05/13 Python
完美解决Pycharm中matplotlib画图中文乱码问题
2021/01/11 Python
苏格兰在线威士忌商店:The Whisky Barrel
2019/05/07 全球购物
VICHY薇姿俄罗斯官方网上商店:法国护肤品牌,火山温泉水
2019/11/22 全球购物
关键字final的用法
2013/10/02 面试题
《乡愁》教学反思
2014/02/18 职场文书
遗嘱公证书标准样本
2014/04/08 职场文书
学校党委副书记个人对照检查材料思想汇报
2014/09/28 职场文书
初中英语教学随笔
2015/08/15 职场文书
加强党性修养心得体会
2016/01/21 职场文书
详解Spring Security如何在权限中使用通配符
2022/06/28 Java/Android