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下intval()和(int)转换使用与区别
Jul 18 PHP
PHP中开发XML应用程序之基础篇 添加节点 删除节点 查询节点 查询节
Jul 09 PHP
php使用CURL伪造IP和来源实例详解
Jan 15 PHP
PHP.ini安全配置检测工具pcc简单介绍
Jul 02 PHP
关于php微信订阅号开发之token验证后自动发送消息给订阅号但是没有消息返回的问题
Dec 21 PHP
Yii2实现让关联字段支持搜索功能的方法
Aug 10 PHP
php版微信小店API二次开发及使用示例
Nov 12 PHP
总结一些PHP中好用但又容易忽略的小知识
Jun 02 PHP
PHP自定义递归函数实现数组转JSON功能【支持GBK编码】
Jul 17 PHP
PhpStorm配置Xdebug调试的方法步骤
Feb 02 PHP
laravel邮件发送的实现代码示例
Jan 31 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的ASP防火墙
2006/10/09 PHP
ecshop 订单确认中显示省市地址信息的方法
2010/03/15 PHP
laravel 5.1下php artisan migrate的使用注意事项总结
2017/06/07 PHP
Smarty模板类内部原理实例分析
2019/07/03 PHP
实例:用 JavaScript 来操作字符串(一些字符串函数)
2007/02/15 Javascript
JavaScript 中的replace方法说明
2007/04/13 Javascript
JS清除选择内容的方法
2015/01/29 Javascript
JavaScript实现向右伸出的多级网页菜单效果
2015/08/25 Javascript
JS产生随机数的用法小结
2016/12/10 Javascript
JS中input表单隐藏域及其使用方法
2017/02/13 Javascript
基于jquery实现九宫格拼图小游戏
2018/11/30 jQuery
详解关于html,css,js三者的加载顺序问题
2019/04/10 Javascript
vue+echarts实现可拖动节点的折线图(支持拖动方向和上下限的设置)
2019/04/12 Javascript
ES6 let和const定义变量与常量的应用实例分析
2019/06/27 Javascript
详解微信小程序支付流程与梳理
2019/07/16 Javascript
Vue js with语句原理及用法解析
2020/09/03 Javascript
[01:20:37]FNATIC vs NIP 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/19 DOTA
python实现简单的计时器功能函数
2015/03/14 Python
Python实现根据IP地址和子网掩码算出网段的方法
2015/07/30 Python
python学生管理系统开发
2019/01/30 Python
Python3.5局部变量与全局变量作用域实例分析
2019/04/30 Python
python判断自身是否正在运行的方法
2019/08/08 Python
python sqlite的Row对象操作示例
2019/09/11 Python
Django 简单实现分页与搜索功能的示例代码
2019/11/07 Python
python列表生成器迭代器实例解析
2019/12/19 Python
vscode写python时的代码错误提醒和自动格式化的方法
2020/05/07 Python
Autopep8的使用(python自动编排工具)
2021/03/02 Python
Belvilla德国:在线预订度假屋
2018/04/10 全球购物
linux系统都有哪些运行级别
2016/03/26 面试题
四年级语文教学反思
2014/02/05 职场文书
答谢会策划方案
2014/05/12 职场文书
学生上课看漫画的检讨书
2014/09/26 职场文书
社会心理学学习心得体会
2016/01/22 职场文书
改进工作作风心得体会
2016/01/23 职场文书
优秀的商业计划书,让融资一步到位
2019/05/07 职场文书
Windows下使用Nginx+Tomcat做负载均衡的完整步骤
2021/03/31 Servers