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 相关文章推荐
怎么样可以把 phpinfo()屏蔽掉?
Nov 24 PHP
php zend 相对路径问题
Jan 12 PHP
劣质的PHP代码简化
Feb 08 PHP
PHP 事务处理数据实现代码
May 13 PHP
fleaphp常用方法分页之Pager使用方法
Apr 23 PHP
php 按指定元素值去除数组元素的实现方法
Nov 04 PHP
mongo Table类文件 获取MongoCursor(游标)的实现方法分析
Jul 01 PHP
php正则匹配html中带class的div并选取其中内容的方法
Jan 13 PHP
php专用数组排序类ArraySortUtil用法实例
Apr 03 PHP
php的crc32函数使用时需要注意的问题(不然就是坑)
Apr 21 PHP
ThinkPHP框架分布式数据库连接方法详解
Mar 14 PHP
Laravel框架实现修改登录和注册接口数据返回格式的方法
Aug 17 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
网页游戏开发入门教程二(游戏模式+系统)
2009/11/02 PHP
apache配置虚拟主机的方法详解
2013/06/17 PHP
2个自定义的PHP in_array 函数,解决大量数据判断in_array的效率问题
2014/04/08 PHP
ThinkPHP3.1新特性之动态设置自动完成和自动验证示例
2014/06/19 PHP
PHP实现批量删除(封装)
2017/04/28 PHP
JavaScript使用cookie
2007/02/02 Javascript
js数组转json并在后台对其解析具体实现
2013/11/20 Javascript
JS+CSS实现下拉列表框美化效果(3款)
2015/08/15 Javascript
JS文字球状放大效果代码分享
2015/08/19 Javascript
AngularJS 中的事件详解
2016/07/28 Javascript
微信小程序报错:this.setData is not a function的解决办法
2017/09/27 Javascript
javascript基于定时器实现进度条功能实例
2017/10/13 Javascript
vue实现文件上传读取及下载功能
2020/11/17 Javascript
基于JavaScript实现控制下拉列表
2020/05/08 Javascript
基于vue实现简易打地鼠游戏
2020/08/21 Javascript
python实现探测socket和web服务示例
2014/03/28 Python
Python实现采用进度条实时显示处理进度的方法
2017/12/19 Python
浅谈Python对内存的使用(深浅拷贝)
2018/01/17 Python
python读写LMDB文件的方法
2018/07/02 Python
如何在python字符串中输入纯粹的{}
2018/08/22 Python
python中实现控制小数点位数的方法
2019/01/24 Python
Python Django实现layui风格+django分页功能的例子
2019/08/29 Python
python爬虫之遍历单个域名
2019/11/20 Python
解决Python3下map函数的显示问题
2019/12/04 Python
使用python实现CGI环境搭建过程解析
2020/04/28 Python
django中related_name的用法说明
2020/05/20 Python
CSS3中的常用选择器使用示例整理
2016/06/13 HTML / CSS
CSS3实现线性渐变用法示例代码详解
2020/08/07 HTML / CSS
HTML5 微格式和相关的属性名称
2010/02/10 HTML / CSS
皮尔·卡丹巴西官方商店:Pierre Cardin
2017/07/21 全球购物
Nordgreen英国官网:斯堪的纳维亚设计师手表
2018/10/24 全球购物
Eton丹麦官网:精美的男式衬衫
2020/05/27 全球购物
路德维希•贝克(LUDWIG BECK)中文官网:德国大型美妆百货
2020/09/19 全球购物
村干部培训班主持词
2014/03/28 职场文书
初中学生期末评语
2014/04/24 职场文书
高中生第一学年自我鉴定
2014/09/12 职场文书