单元测试源模型


10

我的自定义扩展中有几个模型,这些模型仅用于填充实体的添加/编辑形式中的某些选择和/或多项选择。
因此,它们被magento称为“源模型”。
所涉及的值始终相同,并且方法返回的内容相同。
我应该如何对它们进行单元测试?甚至更好,我应该为他们编写单元测试吗?
这是一个例子。
下列类用于添加/编辑表单(称为)的字段type以及同一字段的Grid列。

<?php
namespace Sample\News\Model\Author\Source;

use Magento\Framework\Option\ArrayInterface;

class Type implements ArrayInterface
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;

    /**
     * Get options
     *
     * @return array
     */
    public function toOptionArray()
    {
        $_options = [
            [
                'value' => '',
                'label' => ''
            ],
            [
                'value' => self::COLLABORATOR,
                'label' => __('Collaborator')
            ],
            [
                'value' => self::EMPLOYEE,
                'label' => __('Employee')
            ],
        ];
        return $_options;
    }

    /**
     * get options as key value pair
     *
     * @return array
     */
    public function getOptions()
    {
        $_tmpOptions = $this->toOptionArray();
        $_options = [];
        foreach ($_tmpOptions as $option) {
            $_options[$option['value']] = $option['label'];
        }
        return $_options;
    }
}

Answers:


15

这是一个很棒的话题,我真的很喜欢@KAndy和@fschmengler的答案。
我想补充一些其他问题,这些问题在我问“我应该测试X吗?”之类的问题时会很有用。或“我应该如何测试X?”。

可能出什么问题了?

  • 我可以打一个愚蠢的错字(一直发生),
    这通常不能证明编写测试是合理的。
  • 我会从核心或其他模块复制所需的代码,然后根据需要进行调整吗?
    我发现这样做实际上是非常危险的事情,通常会留下一些细微的错误。在这种情况下,如果测试不太昂贵,我倾向于编写一个测试。实际上,基于源模型进行配置会使它们的IMO风险更大。
  • 可以与其他模块发生冲突吗?
    这几乎仅适用于配置代码。在这种情况下,我希望有一个集成测试来告诉我什么时候发生。
  • Magento可以在将来的版本中更改API吗?
    在这种情况下,这种情况极不可能发生,因为您的代码仅取决于接口。但是涉及到更具体的类,或者如果我的代码扩展了核心类,则这将带来更大的潜在风险。
  • 一个新的PHP版本可能会破坏我的代码。或者也许我想在未来的几年中支持PHP 5.6。
    同样,在这里极不可能,但是在某些情况下,如果以后我要更改代码以使用不兼容的语法,则我想通过测试来警告我。

测试代码有多昂贵?

这有两个方面:

  • 编写测试所需的工作量和时间
  • 测试我将要手动编写的代码段所花费的精力和时间。

在开发某些代码期间,我倾向于不得不经常运行自己编写的代码,直到我认为完成为止。当然,使用单元测试要容易得多。

就您而言,编写测试非常便宜。不需要很多时间或精力。@KAndy是正确的,所有代码都需要维护,但是同样,并非所有测试都需要保留。
这可能是一个示例,其中我将编写一个单元测试,以检查我没有犯错,然后在类完成后将其删除。如果测试不能提供长期价值,我认为删除它们是有道理的。

在选择要编写的测试类型方面,该问题也很重要:例如单元测试或集成测试。

我编写的代码有多有价值?

如果我正在编写的一段代码对于模块提供的服务必不可少,那么无论它多么琐碎,我都会对其进行测试。
如果这只是一种实用工具方法,例如针对UI的应用,而不是业务逻辑的一部分,那么可能就不是。

代码需要更改吗?

随着时间的流逝,我已经习惯了进行测试覆盖,以至于更改未发现的代码感到非常不安全。这包括很简单的事情,例如向源模型添加选项,还包括将类移动到其他文件夹/命名空间或重命名方法。
对此类更改进行测试是非常宝贵的。

需要文件吗?

使用代码有多难?在您的示例中,这是微不足道的,但是在一些更复杂的情况下,进行测试对于其他开发人员(或几个月后本人)的文档记录而言非常有用。

探索与学习

如果我正在编写某些代码,但不确定如何测试,则发现编写测试非常有价值。这个过程几乎总是使我对正在处理的内容有更深入的了解。
对于仍然认为自己正在学习测试的开发人员来说尤其如此。
这也是一个示例,之后删除测试可能很有意义,因为它提供的主要价值是学习。

纪律和压力

坚持红绿重构循环可以帮助我快速上手。在压力下尤其如此。因此,即使某段代码不是真正值得测试的代码,我仍可能会遵循TDD,尤其是在测试代码微不足道的情况下。
这使我时刻保持警惕。

什么以及如何测试?

还要考虑您可以以非常不同的粒度编写测试。

  • 测试确切的返回值。
    这将是一个非常严格的测试,必须针对每个更改进行调整。您是否要破坏测试,例如,如果返回数组中的项目顺序发生变化?
  • 测试返回值的结构。
    对于源模型,这可以将每个子数组检查为两个记录,一个记录带有a label,一个记录带有value键。
  • 检查类工具ArrayInterface
  • getOptions()即使该方法不是已实现接口的一部分,也可以测试该类。

对于可以测试的每种可能的东西,请考虑价值,可维护性和成本。

摘要

总结一下:对于某个代码段是否应该进行测试,没有一个真正的答案。对于每个开发人员,答案都将根据情况而有所不同。


2
好答案!我想强调“当测试不再提供价值时删除测试”部分-有时在最初的开发过程中会有所帮助的旧测试只是阻碍了长期发展。
Fabian Schmengler '16

1
您刚刚回答了我怀疑的另外两个问题。谢谢
Marius

6

我认为,“为源模型编写单元测试,是或否”没有一般性答案。

我已经为源模型编写了单元测试,但是这些是动态的源模型,可以获取外部数据,因此很有意义。

对于只不过是荣耀数组的源模型(如您的示例所示),我不会费心编写单元测试。但是以某种方式,您需要确保没有犯错。有几种选择:

  1. 单元测试
  2. 整合测试
  3. 手动查看配置

您在追踪TDD吗?然后在(1)和(2)之间或两者之间进行选择。否则,请在(2)和(3)之间选择。


如果您将源模型用于系统配置选项,那么集成测试(针对所有配置选项的一项测试)可以呈现配置部分,并检查<select>元素是否存在并包含预期值。请记住,这不是测试一个特定的类,而是测试所有内容是否正确地结合在一起。


就像@KAndy所说的,理想情况下,您不需要那么多样板,只需扩展一个已经过单元测试的基类,然后覆盖一个属性或在外部配置中定义值即可。

在这种情况下,用于具体实现的单元测试不会提供任何附加价值。如果您有许多这样的类,那么ArraySource只要Magento不提供它,最好自己编写一个基类。


使用这样的基类,您的源模型可能如下所示:

class Type extends ArraySource
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;
    protected $elements = [
        self::COLLABORATOR => 'Collaborator',
        self::EMPLOYEE     => 'Employee',
    ];
    protected $withEmpty = true;
    protected $translate = true;
}

感谢您的确认。我将尝试将我赞美的数组转换为可配置的选项列表,但是对于要具有正好N个选项的模型也一样吗?就像“状态”源模型一样。
Marius

我看不到“状态”源模型与您的“作者类型”示例有什么不同
Fabian Schmengler

因为我可能会在前端使用值0和1(作为类常量)来过滤例如启用的实体。我的意思是在这种情况下,选项值后面有逻辑。他们不只是一key=>value对。
Marius

但是这种逻辑不是源模型的一部分,对吗?这意味着在这里没关系。您仍然可以在其他地方使用常量。我添加了一个示例,说明如何使用这种方法。
Fabian Schmengler '16

5

我应该如何对它们进行单元测试?

我认为你不应该。

向系统中添加代码会增加支持和维护成本,但测试过程应精益求精

以上代码不应该存在。我相信,在Magento中,应该使用一种通用的声明方式来定义在不同地方使用的选项集。而且您不愿意为此课程编写测试,这让我感到了不好的代码味


1
谢谢。因此,您说的是这些选项不应硬编码,而应来自配置文件(例如di.xml)。我可以的 但是对于状态源模型,情况是否一样?我的意思是,如果我具有与上述相同的类,但仅在启用和禁用状态(1,0)的情况下,才应该使用相同的配置方法?如果是的话,我想说这对我来说就像是过度设计。
Marius
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.