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 类型转换函数intval
Jun 20 PHP
PHP图片处理类 phpThumb参数用法介绍
Mar 11 PHP
PHP+Memcache实现wordpress访问总数统计(非插件)
Jul 04 PHP
php可应用于面包屑导航的迭代寻找家谱树实现方法
Feb 02 PHP
Laravel中使用阿里云OSS Composer包分享
Feb 10 PHP
YII Framework框架教程之缓存用法详解
Mar 14 PHP
php HTML无刷新提交表单
Apr 05 PHP
Yii2中hasOne、hasMany及多对多关联查询的用法详解
Feb 15 PHP
php提取微信账单的有效信息
Oct 01 PHP
使用PHPUnit进行单元测试并生成代码覆盖率报告的方法
Mar 08 PHP
docker-compose部署php项目实例详解
Jul 30 PHP
PHP设计模式(五)适配器模式Adapter实例详解【结构型】
May 02 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 Pear 安装及使用
2009/03/19 PHP
typecho插件编写教程(五):核心代码
2015/05/28 PHP
PHP利用超级全局变量$_GET来接收表单数据的实例
2016/11/05 PHP
php获取网站根目录物理路径的几种方法(推荐)
2017/03/04 PHP
PHP 多任务秒级定时器的实现方法
2018/05/13 PHP
有关DOM元素与事件的3个谜题
2010/11/11 Javascript
JS弹出对话框返回值代码(asp.net后台)
2010/12/28 Javascript
js判断undefined变量类型使用typeof
2013/06/03 Javascript
JS获取当前使用的浏览器名字以及版本号实现方法
2016/08/19 Javascript
JavaScript 自定义事件之我见
2017/09/25 Javascript
JS运动特效之任意值添加运动的方法分析
2018/01/24 Javascript
Webpack打包字体font-awesome的方法示例
2018/04/26 Javascript
使用 Vue 实现一个虚拟列表的方法
2019/08/20 Javascript
微信小程序实现左侧滑栏过程解析
2019/08/26 Javascript
微信小程序用canvas画图并分享
2020/03/09 Javascript
JS+Canvas实现五子棋游戏
2020/08/26 Javascript
Python实现同时兼容老版和新版Socket协议的一个简单WebSocket服务器
2014/06/04 Python
Python实现的快速排序算法详解
2017/08/01 Python
NetworkX之Prim算法(实例讲解)
2017/12/22 Python
python分批定量读取文件内容,输出到不同文件中的方法
2018/12/08 Python
Python实现插入排序和选择排序的方法
2019/05/12 Python
Python实现微信中找回好友、群聊用户撤回的消息功能示例
2019/08/23 Python
如何基于Python制作有道翻译小工具
2019/12/16 Python
跑步爱好者一站式服务网站:Jack Rabbit
2016/09/01 全球购物
美国豪华的多品牌精品店:The Webster
2019/07/31 全球购物
公司综合部的成员自我评价分享
2013/11/05 职场文书
大学生蛋糕店创业计划书
2014/01/13 职场文书
法院信息化建设方案
2014/05/21 职场文书
党建工作目标管理责任书
2015/01/29 职场文书
行政申诉状范文
2015/05/20 职场文书
教师节简报
2015/07/20 职场文书
描述鲁迅的名言整理,一生受用
2019/08/08 职场文书
利用Python网络爬虫爬取各大音乐评论的代码
2021/04/13 Python
PyTorch的Debug指南
2021/05/07 Python
tomcat下部署jenkins的方法
2022/05/06 Servers
Java使用HttpClient实现文件下载
2022/08/14 Java/Android