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 相关文章推荐
图象函数中的中文显示
Oct 09 PHP
PHP 文件上传功能实现代码
Jun 24 PHP
php强制下载类型的实现代码
Apr 21 PHP
phpMyAdmin 链接表的附加功能尚未激活问题的解决方法(已测)
Mar 27 PHP
PHP最常用的2种设计模式工厂模式和单例模式介绍
Aug 14 PHP
深入PHP异步执行的详解
Jun 03 PHP
浅谈json_encode用法
Mar 05 PHP
PHP加密解密函数详解
Oct 28 PHP
PHP使用pear实现mail发送功能 windows环境下配置pear
Apr 15 PHP
PHP与Java对比学习日期时间函数
Jul 03 PHP
php生成图片缩略图功能示例
Feb 22 PHP
Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例
Apr 04 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
星际RPG字典
2020/03/04 星际争霸
header()函数使用说明
2006/11/23 PHP
PHP上传文件时文件过大$_FILES为空的解决方法
2013/11/26 PHP
PHP中鲜为人知的10个函数
2014/02/28 PHP
php判断数组元素中是否存在某个字符串的方法
2014/06/14 PHP
PHP的cURL库简介及使用示例
2015/02/06 PHP
javascript动画效果类封装代码
2007/08/28 Javascript
JavaScript入门教程(6) Window窗口对象
2009/01/31 Javascript
JavaScript 对象模型 执行模型
2009/12/06 Javascript
鼠标右击事件代码(asp.net后台)
2011/01/27 Javascript
页面加载完成后再执行JS的jquery写法以及区别说明
2014/02/22 Javascript
防止jQuery ajax Load使用缓存的方法小结
2014/02/22 Javascript
JavaScript使用replace函数替换字符串的方法
2015/04/06 Javascript
JavaScript中的函数(二)
2015/12/23 Javascript
一次$.getJSON不执行的简单记录
2016/07/19 Javascript
微信小程序 本地数据读取实例
2017/04/27 Javascript
JS和Canvas实现图片的预览压缩和上传功能
2018/03/30 Javascript
详解微信小程序中组件通讯
2018/10/30 Javascript
Python中在脚本中引用其他文件函数的实现方法
2016/06/23 Python
python 对给定可迭代集合统计出现频率,并排序的方法
2018/10/18 Python
Python3实现二叉树的最大深度
2019/09/30 Python
python3 pathlib库Path类方法总结
2019/12/26 Python
python 画图 图例自由定义方式
2020/04/17 Python
Python在centos7.6上安装python3.9的详细教程(默认python版本为2.7.5)
2020/10/15 Python
html5定位并在百度地图上显示的示例
2014/04/27 HTML / CSS
写给女朋友的道歉信
2014/01/08 职场文书
我爱家乡演讲稿
2014/09/12 职场文书
公司慰问信范文
2015/03/23 职场文书
2016学习雷锋精神活动倡议书
2015/04/27 职场文书
大学生军训感言
2015/08/01 职场文书
诚实守信主题班会
2015/08/13 职场文书
三好学生评选事迹材料(2016精选版)
2016/02/25 职场文书
利用python进行数据加载
2021/06/20 Python
MIME类型中application/xml与text/xml的区别介绍
2022/01/18 HTML / CSS
Nginx工作模式及代理配置的使用细节
2022/03/21 Servers
《宝可梦》动画制作25周年到来 官方发布特别纪念视频
2022/04/01 日漫