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 和 XML: 使用expat函数(一)
Oct 09 PHP
main.php
Dec 09 PHP
wiki-shan写的php在线加密的解密程序
Sep 07 PHP
php str_pad() 将字符串填充成指定长度的字符串
Feb 23 PHP
理解php Hash函数,增强密码安全
Feb 25 PHP
PHP数组传递是值传递而非引用传递概念纠正
Jan 31 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(五)
Jun 23 PHP
YII中assets的使用示例
Jul 31 PHP
php+mysqli实现将数据库中一张表信息打印到表格里的方法
Jan 28 PHP
详解PHP中foreach的用法和实例
Oct 25 PHP
php使用crypt()函数进行加密
Jun 08 PHP
PHP控制循环操作的时间
Apr 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
使用dump函数,给php加断点测试
2013/06/25 PHP
PHP 关于访问控制的和运算符优先级介绍
2013/07/08 PHP
Laravel 创建可以传递参数 Console服务的例子
2019/10/14 PHP
Laravel框架源码解析之入口文件原理分析
2020/05/14 PHP
这些年、我收集的JQuery代码小结
2012/08/01 Javascript
JavaScript高级程序设计(第3版)学习笔记5 js语句
2012/10/11 Javascript
jQuery基本选择器选择元素使用介绍
2013/04/18 Javascript
浅析Javascript使用include/require
2013/11/13 Javascript
JsRender实用入门教程
2014/10/31 Javascript
JavaScript实现定时隐藏与显示图片的方法
2015/08/06 Javascript
jquery移动端TAB触屏切换实现效果
2020/12/22 Javascript
微信小程序 实现动态显示和隐藏某个控件
2017/04/27 Javascript
AngularJS 中ui-view传参的实例详解
2017/08/25 Javascript
快速搭建React的环境步骤详解
2017/11/06 Javascript
angular 内存溢出的问题解决
2018/07/12 Javascript
node解析修改nginx配置文件操作实例分析
2019/11/06 Javascript
Node.js API详解之 readline模块用法详解
2020/05/22 Javascript
[04:03]DOTA2英雄梦之声_第02期_风暴之灵
2014/06/30 DOTA
[33:15]2018DOTA2亚洲邀请赛3月30日 小组赛B组 VP VS Mineski
2018/03/31 DOTA
[00:32]2018DOTA2亚洲邀请赛出场——VP
2018/04/04 DOTA
学习python (1)
2006/10/31 Python
python下10个简单实例代码
2017/11/15 Python
详谈python3中用for循环删除列表中元素的坑
2018/04/19 Python
pycharm的console输入实现换行的方法
2019/01/16 Python
python中round函数如何使用
2020/06/19 Python
静态变量和实例变量的区别
2015/07/07 面试题
党的群众路线教育实践活动批评与自我批评
2014/02/16 职场文书
承诺书样本
2014/08/30 职场文书
小学生迎国庆演讲稿
2014/09/05 职场文书
2014年妇女工作总结
2014/12/06 职场文书
工作时间调整通知
2015/04/24 职场文书
公司年夜饭通知
2015/04/25 职场文书
初三英语教学反思
2016/02/15 职场文书
实习报告怎么写
2019/06/20 职场文书
使用qt quick-ListView仿微信好友列表和聊天列表的示例代码
2021/06/13 Python
微信小程序APP页面的之间的相互传递参数以及自定义组件
2022/04/19 Javascript