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 相关文章推荐
WIN98下Apache1.3.14+PHP4.0.4的安装
Oct 09 PHP
如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量的
Mar 06 PHP
Session保存到数据库的php类分享
Oct 24 PHP
解析php curl_setopt 函数的相关应用及介绍
Jun 17 PHP
基于php使用memcache存储session的详解
Jun 25 PHP
thinkphp模板的包含与渲染实例分析
Nov 26 PHP
Laravel 5框架学习之Eloquent 关系
Apr 09 PHP
php使用ZipArchive函数实现文件的压缩与解压缩
Oct 27 PHP
php处理复杂xml数据示例
Jul 11 PHP
简述php环境搭建与配置
Dec 05 PHP
php中file_get_contents()函数用法实例
Feb 21 PHP
PHP实现的只保留字符串首尾字符功能示例【隐藏部分字符串】
Mar 11 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 只允许指定IP访问(允许*号通配符过滤IP)
2014/07/08 PHP
两款万能的php分页类
2015/11/12 PHP
php实现留言板功能(会话控制)
2017/05/23 PHP
iframe异步加载实现点击左边菜单加载右边内容实例讲解
2013/03/04 Javascript
HTML5之lang属性与dir属性的详解
2013/06/19 Javascript
js去除空格的12种实用方法
2013/11/08 Javascript
window.open()详解及浏览器兼容性问题示例探讨
2014/05/29 Javascript
jQuery中使用animate自定义动画的方法
2016/05/29 Javascript
webstorm添加*.vue文件支持
2018/05/08 Javascript
Bootstrap table中toolbar新增条件查询及refresh参数使用方法
2018/05/18 Javascript
浅谈Vue数据响应思路之数组
2018/11/06 Javascript
vue-cli4.x创建企业级项目的方法步骤
2020/06/18 Javascript
Vue.js使用axios动态获取response里的data数据操作
2020/09/08 Javascript
详解ES6中class的实现原理
2020/10/03 Javascript
如何利用vue实现波谱拟合详解
2020/11/05 Javascript
[05:24]TI9采访——教练
2019/08/24 DOTA
Python 错误和异常小结
2013/10/09 Python
Python greenlet实现原理和使用示例
2014/09/24 Python
运动检测ViBe算法python实现代码
2018/01/09 Python
详解如何利用Cython为Python代码加速
2018/01/27 Python
通过 Django Pagination 实现简单分页功能
2019/11/11 Python
Python异步编程之协程任务的调度操作实例分析
2020/02/01 Python
基于PyTorch中view的用法说明
2021/03/03 Python
6PM官网:折扣鞋、服装及配饰
2018/08/03 全球购物
Perfume’s Club意大利官网:欧洲美妆电商
2019/05/03 全球购物
世嘉游戏英国官方商店:SEGA Shop UK
2019/09/20 全球购物
Java中的基本数据类型所占存储空间大小固定的吗
2012/02/15 面试题
园艺师求职信
2014/03/10 职场文书
《故乡》教学反思
2014/04/10 职场文书
奥巴马英文演讲稿
2014/05/15 职场文书
小学安全工作汇报材料
2014/08/19 职场文书
大三学生英语考试作弊检讨书
2015/01/01 职场文书
2015年财务科工作总结范文
2015/05/13 职场文书
2019暑假学生安全口号
2019/06/27 职场文书
2019财务管理制度最新范本!
2019/07/09 职场文书
导游词之鲁迅祖居
2019/10/17 职场文书