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基础知识:类与对象(5) static
Dec 13 PHP
Godaddy空间Zend Optimizer升级方法
May 10 PHP
php读取文件内容的几种方法详解
Jun 26 PHP
php动态生成函数示例
Mar 21 PHP
PHP+MySQL删除操作实例
Jan 21 PHP
PHP准确取得服务器IP地址的方法
Jun 02 PHP
smarty模板数学运算示例
Dec 11 PHP
php使用include 和require引入文件的区别
Feb 16 PHP
Nginx下ThinkPHP5的配置方法详解
Aug 01 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
Sep 16 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
Nov 14 PHP
laravel入门知识点整理
Sep 15 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
神族 Protoss 剧情介绍
2020/03/14 星际争霸
php单态设计模式(单例模式)实例
2014/11/18 PHP
php中引用&amp;的用法分析【变量引用,函数引用,对象引用】
2016/12/12 PHP
PHP中将一个字符串部分字符用星号*替代隐藏的实现代码
2019/09/08 PHP
JavaScript Accessor实现说明
2010/12/06 Javascript
jQuery之尺寸调整组件的深入解析
2013/06/19 Javascript
javascript使用正则获取url上的某个参数
2014/09/04 Javascript
jQuery中prev()方法用法实例
2015/01/08 Javascript
Javascript数据结构与算法之列表详解
2015/03/12 Javascript
jQuery滚动加载图片实现原理
2015/12/14 Javascript
AngularJS控制器详解及示例代码
2016/08/16 Javascript
javascript 中select框触发事件过程的分析
2017/08/01 Javascript
基于vue的短信验证码倒计时demo
2017/09/13 Javascript
微信小程序实现下载进度条的方法
2017/12/08 Javascript
基于cropper.js封装vue实现在线图片裁剪组件功能
2018/03/01 Javascript
详解Vue的钩子函数(路由导航守卫、keep-alive、生命周期钩子)
2018/07/24 Javascript
vue.extend与vue.component的区别和联系
2018/09/19 Javascript
详解JavaScript中的坐标和距离
2019/05/27 Javascript
基于layui框架响应式布局的一些使用详解
2019/09/16 Javascript
vue+ts下对axios的封装实现
2020/02/18 Javascript
详解Vue中的MVVM原理和实现方法
2020/07/15 Javascript
浅谈JavaScript中this的指向更改
2020/07/28 Javascript
python中requests使用代理proxies方法介绍
2017/10/25 Python
NumPy排序的实现
2020/01/21 Python
Python换行与不换行的输出实例
2020/02/19 Python
如何用python批量调整视频声音
2020/12/22 Python
CSS3制作半透明边框(Facebox)类似渐变
2012/12/09 HTML / CSS
中东奢侈品市场:Coveti
2019/05/12 全球购物
软件配置管理有什么好处
2015/04/15 面试题
电信专业应届生自荐信
2013/09/28 职场文书
酒店秘书求职信范文
2014/02/17 职场文书
2014年小学国庆节活动方案
2014/09/16 职场文书
农村党员对照检查材料
2014/09/24 职场文书
对党的十八届四中全会的期盼
2014/10/17 职场文书
教师年度个人总结
2015/02/11 职场文书
简历中自我评价范文
2015/03/11 职场文书