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 相关文章推荐
粗略计算在线时间,bug:ip相同
Dec 09 PHP
PHP clearstatcache()函数详解
Mar 02 PHP
PHP防CC攻击实现代码
Dec 29 PHP
php tp验证表单与自动填充函数代码
Feb 22 PHP
php中计算未知长度的字符串哪个字符出现的次数最多的代码
Aug 14 PHP
Laravel 4 初级教程之安装及入门
Oct 30 PHP
php计算两个文件相对路径的方法
Mar 14 PHP
php提取身份证号码中的生日日期以及验证是否为成年人的函数
Sep 29 PHP
PHP获取昨天、今天及明天日期的方法
Feb 03 PHP
php str_replace替换指定次数的方法详解
May 05 PHP
php类自动装载、链式操作、魔术方法实现代码
Jul 23 PHP
PHP终止脚本运行三种实现方法详解
Sep 01 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
mysql建立外键
2006/11/25 PHP
PHP得到mssql的存储过程的输出参数功能实现
2012/11/23 PHP
php中解析带中文字符的url函数分享
2015/01/20 PHP
php+html5使用FormData对象提交表单及上传图片的方法
2015/02/11 PHP
php计算到指定日期还有多少天的方法
2015/04/14 PHP
PHP通过文件路径获取文件名的实例代码
2018/10/14 PHP
javascript中onmouse事件在div中失效问题的解决方法
2012/01/09 Javascript
纯JS实现动态时间显示代码
2014/02/08 Javascript
按钮接受回车事件的三种实现方法
2014/06/06 Javascript
js/jquery判断浏览器的方法小结
2014/09/02 Javascript
浅析js预加载/延迟加载
2014/09/25 Javascript
JavaScript动态创建link标签到head里的方法
2014/12/22 Javascript
JavaScript脚本判断蜘蛛来源的方法
2015/09/22 Javascript
JS中substring与substr的用法
2016/11/16 Javascript
Angular排序实例详解
2017/06/28 Javascript
详解vue-cli3多环境打包配置
2019/03/28 Javascript
vue19 组建 Vue.extend component、组件模版、动态组件 的实例代码
2019/04/04 Javascript
ES6入门教程之let、const的使用方法
2019/04/13 Javascript
vue+ts下对axios的封装实现
2020/02/18 Javascript
[01:14]DOTA2亚洲邀请赛 ShowOpen
2015/02/07 DOTA
[01:38]DOTA2辉夜杯 欢乐的观众现场采访
2015/12/26 DOTA
python实现的重启关机程序实例
2014/08/21 Python
python编程开发之textwrap文本样式处理技巧
2015/11/13 Python
对python 中re.sub,replace(),strip()的区别详解
2019/07/22 Python
python numpy存取文件的方式
2020/04/01 Python
python实现udp传输图片功能
2020/03/20 Python
学校司机岗位职责
2013/11/14 职场文书
政法大学毕业生自荐信范文
2014/01/01 职场文书
市场拓展计划书
2014/05/03 职场文书
献爱心捐款倡议书
2014/05/14 职场文书
2014年司法所工作总结
2014/11/22 职场文书
2014年行政执法工作总结
2014/12/11 职场文书
教你快速开启Apache SkyWalking的自监控
2021/04/25 Servers
MyBatis 动态SQL全面详解
2021/10/05 MySQL
使用python求解迷宫问题的三种实现方法
2022/03/17 Python
如何在Python中妥善使用进度条详解
2022/04/05 Python