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 相关文章推荐
PHP4 与 MySQL 数据库操作函数详解
Oct 09 PHP
一些PHP写的小东西
Dec 06 PHP
php预定义常量
Dec 25 PHP
MYSQL环境变量设置方法
Jan 15 PHP
解决phpmyadmin中缺少mysqli扩展问题的方法
May 06 PHP
通过PHP current函数获取未知字符键名数组第一个元素的值
Jun 24 PHP
php管理nginx虚拟主机shell脚本实例
Nov 19 PHP
在Mac OS上编译安装Nginx+PHP+MariaDB开发环境的教程
Feb 23 PHP
PHP获取当前文件的父目录方法汇总
Jul 21 PHP
yii2 commands模式以及配置crontab定时任务的方法
Aug 19 PHP
php7性能提升的原因详解
Oct 13 PHP
php查看一个变量的占用内存的实例代码
Mar 29 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读取der格式证书乱码解决方法
2015/06/22 PHP
CI框架数据库查询缓存优化的方法
2016/11/21 PHP
php PDO属性设置与操作方法分析
2018/12/27 PHP
创建公共调用 jQuery Ajax 带返回值
2012/08/01 Javascript
javascript 文件的同步加载与异步加载实现原理
2012/12/13 Javascript
javaScript 动态访问JSon元素示例代码
2013/08/30 Javascript
PHP+jQuery实现随意拖动层并即时保存拖动位置
2015/04/30 Javascript
Bootstrap每天必学之按钮(一)
2015/11/24 Javascript
理解javascript定时器中的setTimeout与setInterval
2016/02/23 Javascript
深入剖析JavaScript中的函数currying柯里化
2016/04/29 Javascript
Bootstrap3使用typeahead插件实现自动补全功能
2016/07/07 Javascript
Vue.js仿Metronic高级表格(一)静态设计
2017/04/17 Javascript
js学习总结之dom2级事件基础知识详解
2017/07/27 Javascript
react写一个select组件的实现代码
2019/04/03 Javascript
vue路由跳转传参数的方法
2019/05/06 Javascript
Vue切换div显示隐藏,多选,单选代码解析
2020/07/14 Javascript
Python的dict字典结构操作方法学习笔记
2016/05/07 Python
Python实现快速排序算法及去重的快速排序的简单示例
2016/06/26 Python
使用Python的turtle模块画图的方法
2017/11/15 Python
python爬虫爬取快手视频多线程下载功能
2018/02/28 Python
python 删除字符串中连续多个空格并保留一个的方法
2018/12/22 Python
使用Django和Postgres进行全文搜索的实例代码
2020/02/13 Python
国际书籍零售商:Wordery
2017/11/01 全球购物
英国最大的网上药品商店:Chemist Direct
2017/12/16 全球购物
Shoes For Crews法国官网:美国领先的防滑鞋设计和制造商
2018/01/01 全球购物
为有想象力的人提供的生活方式商店:Firebox
2018/06/04 全球购物
可持续木材、生态和铝制太阳镜:Proof Eyewear
2019/07/24 全球购物
奥地利手表、香水、化妆品和珠宝购物网站:Brasty.at
2021/01/17 全球购物
十岁生日家长答谢词
2014/01/17 职场文书
公司应聘自荐书
2014/06/14 职场文书
经典毕业生求职信
2014/07/12 职场文书
英语导游词
2015/02/13 职场文书
心术观后感
2015/06/11 职场文书
导游词之青城山景区
2019/09/27 职场文书
如何在centos上使用yum安装rabbitmq-server
2021/03/31 Servers
vue3获取当前路由地址
2022/02/18 Vue.js