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实现小型站点广告管理(修正版)
Oct 09 PHP
php下通过curl抓取yahoo boss 搜索结果的实现代码
Jun 10 PHP
php获取数组中重复数据的两种方法
Jun 28 PHP
php 启动报错如何解决
Jan 17 PHP
PHP中Fatal error session_start()错误解决步骤
Aug 05 PHP
php中fgetcsv()函数用法实例
Nov 28 PHP
php和editplus正则表达式去除空白行
Apr 17 PHP
PHP中使用substr()截取字符串出现中文乱码问题该怎么办
Oct 21 PHP
php 实现一个字符串加密解密的函数实例代码
Nov 01 PHP
php的laravel框架快速集成微信登录的方法
Dec 12 PHP
PHP单例模式定义与使用实例详解
Feb 06 PHP
全面解析PHP面向对象的三大特征
Jun 10 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读取Excel并导入数据库实例
2014/03/02 PHP
Linux下安装oracle客户端并配置php5.3
2014/10/12 PHP
php上传文件常见问题总结
2015/02/03 PHP
php实现倒计时效果
2015/12/19 PHP
laravel5.2实现区分前后台用户登录的方法
2017/01/11 PHP
PHP实现的简单对称加密与解密方法实例小结
2017/08/28 PHP
BOOM vs RR BO5 第四场 2.14
2021/03/10 DOTA
jQuery ajax BUG:object doesn't support this property or method
2010/07/06 Javascript
jQuery动态显示和隐藏datagrid中的某一列的方法
2013/12/11 Javascript
JS调试必备的5个debug技巧
2014/03/07 Javascript
排序算法的javascript实现与讲解(99js手记)
2014/09/28 Javascript
AngularJS身份验证的方法
2016/02/17 Javascript
微信小程序小组件 基于Canvas实现直播点赞气泡效果
2020/05/29 Javascript
Three.js如何实现雾化效果示例代码
2017/09/27 Javascript
vue 父组件中调用子组件函数的方法
2019/06/06 Javascript
微信公众号服务器验证Token步骤图解
2019/12/30 Javascript
谈谈node.js中的模块系统
2020/09/01 Javascript
python实现端口转发器的方法
2015/03/13 Python
Python编程中用close()方法关闭文件的教程
2015/05/24 Python
python字符串替换第一个字符串的方法
2019/06/26 Python
Python用SSH连接到网络设备
2021/02/18 Python
CSS3 创建网页动画实现弹跳球动效果
2018/10/30 HTML / CSS
浅析图片上传及canvas压缩的流程
2020/06/10 HTML / CSS
美国知名保健品网站:LuckyVitamin(支持中文)
2017/08/09 全球购物
实习教师个人的自我评价
2013/11/08 职场文书
护理专业自荐书
2014/06/04 职场文书
2014迎国庆标语大全
2014/09/19 职场文书
四风自我剖析材料思想汇报
2014/10/01 职场文书
2015年酒店年度工作总结
2015/05/23 职场文书
2016年党员干部公开承诺书
2016/03/24 职场文书
创业计划书之溜冰场
2019/10/25 职场文书
祝福语集锦:给满月宝宝的祝福语
2019/11/20 职场文书
详解RedisTemplate下Redis分布式锁引发的系列问题
2021/04/27 Redis
html+css实现分层金字塔的实例
2021/06/02 HTML / CSS
React列表栏及购物车组件使用详解
2021/06/28 Javascript
Pycharm远程调试和MySQL数据库授权问题
2022/03/18 MySQL