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 相关文章推荐
phpMyadmin 用户权限中英对照
Apr 02 PHP
mysql下创建字段并设置主键的php代码
May 16 PHP
php 转换字符串编码 iconv与mb_convert_encoding的区别说明
Nov 10 PHP
PHP学习散记_编码(json_encode 中文不显示)
Nov 10 PHP
用PHP实现小写金额转换大写金额的代码(精确到分)
Jan 10 PHP
PHP彩蛋信息介绍和阻止泄漏的方法(隐藏功能)
Aug 06 PHP
Yii2如何批量添加数据
May 17 PHP
PHP数组相加操作及与array_merge的区别浅析
Nov 26 PHP
PHP实现按之字形顺序打印二叉树的方法
Jan 16 PHP
yii2中关于加密解密的那些事儿
Jun 12 PHP
php转换上传word文件为PDF的方法【基于COM组件】
Jun 10 PHP
laravel中数据显示方法(默认值和下拉option默认选中)
Oct 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 代码规范小结
2012/03/08 PHP
利用PHP实现短域名互转
2013/07/05 PHP
PHP设计模式之数据访问对象模式(DAO)原理与用法实例分析
2019/12/12 PHP
laravel框架select2多选插件初始化默认选中项操作示例
2020/02/18 PHP
PHP哈希表实现算法原理解析
2020/12/11 PHP
window.location.hash 使用说明
2010/11/08 Javascript
jQuery ReferenceError: $ is not defined 错误的处理办法
2013/05/10 Javascript
运用JQuery的toggle实现网页加载完成自动弹窗
2014/03/18 Javascript
jquery对象和javascript对象即DOM对象相互转换
2014/08/07 Javascript
JS模式之单例模式基本用法
2015/06/30 Javascript
谈谈encodeURI和encodeURIComponent以及escape的区别与应用
2015/11/24 Javascript
详解js中Number()、parseInt()和parseFloat()的区别
2016/12/20 Javascript
Vue2.0如何发布项目实战
2017/07/27 Javascript
使用JavaScript实现表格编辑器(实例讲解)
2017/08/02 Javascript
Vue源码学习之初始化模块init.js解析
2017/11/02 Javascript
vue2.0在没有dev-server.js下的本地数据配置方法
2018/02/23 Javascript
JS 正则表达式验证密码、邮箱格式的实例代码
2018/10/28 Javascript
关于element-ui的隐藏组件el-scrollbar的使用
2019/05/29 Javascript
jquery 键盘事件 keypress() keydown() keyup()用法总结
2019/10/23 jQuery
vue项目使用.env文件配置全局环境变量的方法
2019/10/24 Javascript
[01:10]DOTA2次级职业联赛 - Fly战队宣传片
2014/12/01 DOTA
Python ORM框架SQLAlchemy学习笔记之关系映射实例
2014/06/10 Python
Python sys.argv用法实例
2015/05/28 Python
JSON Web Tokens的实现原理
2017/04/02 Python
python批量修改文件编码格式的方法
2018/05/31 Python
Django中数据库的数据关系:一对一,一对多,多对多
2018/10/21 Python
浅谈python在提示符下使用open打开文件失败的原因及解决方法
2018/11/30 Python
Django如何使用第三方服务发送电子邮件
2019/08/14 Python
Python安装tar.gz格式文件方法详解
2020/01/19 Python
Python实现对adb命令封装
2020/03/06 Python
Python 字节流,字符串,十六进制相互转换实例(binascii,bytes)
2020/05/11 Python
详解python的变量缓存机制
2021/01/24 Python
解决tensorflow模型压缩的问题_踩坑无数,总算搞定
2021/03/02 Python
Ralph Lauren英国官方网站:Ralph Lauren UK
2018/04/03 全球购物
分析MySQL抛出异常的几种常见解决方式
2021/05/18 MySQL
Pytorch反向传播中的细节-计算梯度时的默认累加操作
2021/06/05 Python