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 相关文章推荐
利用curl 多线程 模拟 并发的详解
Jun 14 PHP
php根据isbn书号查询amazon网站上的图书信息的示例
Feb 13 PHP
php的declare控制符和ticks教程(附示例)
Mar 21 PHP
php实现过滤字符串中的中文和数字实例
Jul 29 PHP
php封装的连接Mysql类及用法分析
Dec 10 PHP
php实现的xml操作类
Jan 15 PHP
JSON两种结构之对象和数组的理解
Jul 19 PHP
PHP简单计算两个时间差的方法示例
Jun 20 PHP
使用YII2框架实现微信公众号中表单提交功能
Sep 04 PHP
详解laravel安装使用Passport(Api认证)
Jul 27 PHP
PHP实现通过二维数组键值获取一维键名操作示例
Oct 11 PHP
PHP与Web页面的交互示例详解二
Aug 04 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 连接mssql数据库 初学php笔记
2010/03/01 PHP
PHP 万年历实现代码
2012/10/18 PHP
php实现随机显示图片方法汇总
2015/05/21 PHP
WordPress迁移时一些常见问题的解决方法整理
2015/11/24 PHP
PHPCMS V9 添加二级导航的思路详解
2016/10/20 PHP
在laravel框架中使用model层的方法
2019/10/08 PHP
jQuery获取当前对象标签名称的方法
2014/02/07 Javascript
SyntaxHighlighter 3.0.83使用笔记
2015/01/26 Javascript
原生js结合html5制作简易的双色子游戏
2015/03/30 Javascript
JavaScript调用浏览器打印功能实例分析
2015/07/17 Javascript
jQuery中ajax的load()与post()方法实例详解
2016/01/05 Javascript
jQuery筛选数组之grep、each、inArray、map的用法及遍历json对象
2016/06/20 Javascript
教你快速搭建Node.Js服务器的方法教程
2017/03/30 Javascript
微信小程序文章详情页面实现代码
2018/09/10 Javascript
对angular4子路由&amp;辅助路由详解
2018/10/09 Javascript
使用Angular 6创建各种动画效果的方法
2018/10/10 Javascript
vue项目中全局引入1个.scss文件的问题解决
2019/08/01 Javascript
如何修改Vue打包后文件的接口地址配置的方法
2020/04/22 Javascript
Python程序中使用SQLAlchemy时出现乱码的解决方案
2015/04/24 Python
python获得linux下所有挂载点(mount points)的方法
2015/04/29 Python
Python遍历文件夹和读写文件的实现代码
2016/08/28 Python
python绘制铅球的运行轨迹代码分享
2017/11/14 Python
每天迁移MySQL历史数据到历史库Python脚本
2018/04/13 Python
Python基于pandas实现json格式转换成dataframe的方法
2018/06/22 Python
对python借助百度云API对评论进行观点抽取的方法详解
2019/02/21 Python
Python 硬币兑换问题
2019/07/29 Python
wxPython+Matplotlib绘制折线图表
2019/11/19 Python
在Python中实现函数重载的示例代码
2019/12/12 Python
AmazeUI 模态窗口的实现代码
2020/08/18 HTML / CSS
阿迪达斯越南官网:adidas越南
2020/07/19 全球购物
十岁生日同学答谢词
2014/01/19 职场文书
交通事故协议书
2014/04/15 职场文书
社区党支部承诺书
2015/04/29 职场文书
房屋质量投诉书
2015/07/02 职场文书
发言稿之优秀教师篇
2019/09/26 职场文书
详解Python小数据池和代码块缓存机制
2021/04/07 Python