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 相关文章推荐
IIS环境下快速安装、配置和调试PHP5.2.0
Dec 17 PHP
php 前一天或后一天的日期
Jun 28 PHP
php算开始时间到过期时间的相隔的天数
Jan 12 PHP
如何取得中文字符串中出现次数最多的子串
Aug 08 PHP
2个自定义的PHP in_array 函数,解决大量数据判断in_array的效率问题
Apr 08 PHP
ThinkPHP模板中数组循环实例
Oct 30 PHP
PHP面向对象之后期静态绑定功能介绍
May 18 PHP
php使用ffmpeg获取视频信息并截图的实现方法
May 03 PHP
PHP构造函数与析构函数用法示例
Sep 28 PHP
phpStudy2016 配置多个域名期间遇到的问题小结
Oct 19 PHP
CMSPRESS 10行代码搞定 PHP无限级分类2
Mar 30 PHP
PHP ob缓存以及ob函数原理实例解析
Nov 13 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
Drupal 添加模块出现莫名其妙的错误的解决方法(往往出现在模块较多时)
2011/04/18 PHP
php自定义函数call_user_func和call_user_func_array详解
2011/07/14 PHP
总结的一些PHP开发中的tips(必看篇)
2017/03/24 PHP
ThinkPHP框架实现导出excel数据的方法示例【基于PHPExcel】
2018/05/12 PHP
js动画(animate)简单引擎代码示例
2012/12/04 Javascript
jquery解决图片路径不存在执行替换路径
2013/02/06 Javascript
JS弹出可拖拽可关闭的div层完整实例
2015/02/13 Javascript
JS实现支持Ajax验证的表单插件
2016/03/24 Javascript
Bootstrap每天必学之响应式导航、轮播图
2016/04/25 Javascript
在微信、支付宝、百度钱包实现点击返回按钮关闭当前页面和窗口的方法
2016/08/05 Javascript
浅析JavaScript中break、continue和return的区别
2016/11/30 Javascript
jquery.pagination.js分页使用教程
2018/10/23 jQuery
详解JavaScript的变量
2019/04/04 Javascript
vue h5移动端禁止缩放代码
2019/10/28 Javascript
Vue的双向数据绑定实现原理解析
2020/02/17 Javascript
让Vue响应Map或Set的变化操作
2020/11/11 Javascript
python绘图库Matplotlib的安装
2014/07/03 Python
Python中format()格式输出全解
2019/04/12 Python
python实现微信定时每天和女友发送消息
2019/04/29 Python
sklearn-SVC实现与类参数详解
2019/12/10 Python
python多维数组分位数的求取方式
2020/03/03 Python
使用Python爬取Json数据的示例代码
2020/12/07 Python
GOLFINO英国官网:高尔夫服装
2020/04/11 全球购物
会计毕业生自荐信
2013/11/21 职场文书
大学考试作弊检讨书
2014/01/30 职场文书
《从现在开始》教学反思
2014/04/15 职场文书
商务英语专业毕业生求职信
2014/07/06 职场文书
民族学专业职业生涯规划范文:积跬步以至千里
2014/09/11 职场文书
无房产证房屋转让协议书合同样本
2014/10/18 职场文书
工作保证书
2015/01/17 职场文书
中国合伙人观后感
2015/06/02 职场文书
转学证明范本
2015/06/19 职场文书
安全事故隐患排查治理制度
2015/08/05 职场文书
不要在HTML中滥用div
2021/05/08 HTML / CSS
90后经典动画片排行:《数码宝贝》第二,《小鲤鱼历险记》在榜
2022/03/18 日漫
vue3使用vuedraggable实现拖拽功能
2022/04/06 Vue.js