【问题标题】:Access protected properties via public method通过公共方法访问受保护的属性
【发布时间】:2018-03-28 23:37:09
【问题描述】:

我正在通过模拟一个类并测试函数调用来测试逻辑流。

function() setUp() 
{
    $this->shipping_method = $this->getMockBuilder(Wc_Trincargo_Shipping_Method::class)
                    ->getMock();

    $this->shipping_method->set_post_data([
        'woocommerce_wc-trinicargo-shipping_waybill_password' => 'xxx',
        'woocommerce_wc-trinicargo-shipping_waybill_username' => 'xxxx',
        'woocommerce_wc-trinicargo-shipping_waybill_customer_id' => uniqid(),
        'woocommerce_wc-trinicargo-shipping_waybill_pickupdays' => 2
    ]);
}

set_post_data 是一个设置 protected 属性的公共方法。

稍后我测试调用另一个需要检查上述 protected 属性的方法。我知道他们说你不能模拟受保护的和私有的属性,但如果这些属性是由公共方法设置的......难道不应该吗?

【问题讨论】:

  • 您不应该知道该方法首先设置了受保护的属性。这有点不对。这是该方法的内部实现,您在测试时不应该意识到这一点。
  • @RoyalBg 不确定你在这里想说什么。
  • 我想说的是,您根据业务规范测试方法,而不是根据背后代码的知识。这是错误的。想象一下,您正在测试一个编译好的库并且不知道代码的内部行为,您只知道它的行为是由业务决定的。
  • 最初我正在测试所述方法的逻辑,但是由于未设置所述属性而导致测试失败,如果这有意义的话,这并不是测试的一部分
  • 哦,这完全不同。检查是否有专门的框架来测试 woocommerce。他们自己在项目中进行了测试 - 检查他们是如何做到的。

标签: php phpunit mockery


【解决方案1】:

如果您确实需要在测试中访问受保护的属性并且您没有 getter(也不应该纯粹为测试创建一个),则可以使用反射。

<?php

class MyClass
{
    protected $myProperty;

    public function setMyProperty($value) 
    {
        $this->myProperty = $value;
    }
}


$a = new MyClass();
$a->setMyProperty('TestValue');

// echo $a->myProperty; Can't do this because it's protected.

$r = new ReflectionProperty('MyClass', 'myProperty');
$r->setAccessible(true);
$value = $r->getValue($a);

echo $value; // 'TestValue'

【讨论】:

    【解决方案2】:

    protected 属性来自 public 方法是无关紧要的。可见性无论如何都属于该属性。如果您之后需要检查该属性的值,请为该属性创建一个公共 getter。

    请注意,测试简单的 getter 和 setter 是没有用的。您应该测试该类对属性值的作用。

    无用测试示例:

    class MyClass {
        private $prop;
        public function setProp($value) {
            $this->prop = $value;
        }
        public function getProp() {
            return $this->prop;
        }
    }
    

    测试

    $myClass = new MyClass();
    $myClass->setProp('foo');
    assertTrue($myClass->getProp() === 'foo');
    

    以一种有意义的方式使用 prop 的类示例,它决定了另一个方法的行为/输出:

    class MyClass2 {
        private $prop;
        public function setProp($value) {
            $this->prop = $value;
        }
        public function getPropUpperCase() {
            return strtoupper($this->prop);
        }
    }
    

    测试

    $myClass2 = new MyClass();
    $myClass2->setProp('foo');
    assertTrue($myClass->getPropUpperCase() === 'FOO');
    

    【讨论】:

    • 更改类定义以满足测试的需要对我来说似乎是错误的做法。如果此属性没有getX() 并且其设置为受保护,则很可能有这样做的原因。纯粹添加一个额外的方法以便他可以在测试中访问它并不是真正可取的。如果没有原因,那么显然只是添加一个 getter,这是最简单的解决方案。
    • @Pigeon 测试的前提是有缺陷的。如果属性值在被公共设置器占用后受到保护,需要检查属性值吗?测试的布局全错了。
    • @localheinz 在某些奇怪的边缘情况下可能是这种情况,但您可以使该字段不可变并且只允许设置一次。当您测试您的实际功能时,that 函数的输出将反映属性的值。除非是最后的选择,否则通常会避免使用反射器类。我的观点是,测试(隐藏)属性本身的价值是没有用的。该属性 for 的用途是应该测试的。订阅 TDD 并不意味着为类中的每个属性创建单元测试或模拟。
    • 如果检索到属性,则检查方法的结果
    • @Kendall 您在这里的问题似乎是 x 对 y 的问题。你可以使用反射器类来做你想做的事,或者你可以陈述你的实际问题,因为看起来你的架构有一些缺陷。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-31
    • 2011-10-11
    • 2012-12-28
    • 2014-09-13
    • 1970-01-01
    • 2020-09-06
    相关资源
    最近更新 更多