单元测试 (PHP):何时伪造/模拟依赖项,何时不 [英] Unit Testing (PHP): When to fake/mock dependencies and when not to

查看:24
本文介绍了单元测试 (PHP):何时伪造/模拟依赖项,何时不的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为单元测试伪造依赖项(例如 Doctrine)还是使用真实的依赖项更好?

is it better to fake dependencies (for example Doctrine) for unit-tests or to use the real ones?

推荐答案

在单元测试中,您只使用一个类的真实实例,这就是您要测试的类.

In a unit test, you use only ONE real instance of a class, and that is the class that you want to test.

应该模拟该类的所有依赖项,除非有理由不这样做.

ALL dependencies of that class should be mocked, unless there is a reason not to.

不模拟的原因是如果使用的数据对象本身没有依赖关系 - 您可以使用真实对象并测试它之后是否收到正确的数据.

Reasons not to mock would be if data objects are being used that have no dependencies itself - you can use the real object and test if it received correct data afterwards.

另一个不模拟的原因是如果模拟的配置太复杂——在这种情况下,你有理由重构代码,因为如果模拟一个类太复杂,那个类的 API 可能是太复杂了.

Another reason not to mock would be if the configuration of the mock is too complicated - in that case, you have a reason to refactor the code instead, because if mocking a class is too complicated, the API of that class might be too complicated, too.

但一般的答案是:您希望每次都模拟每个依赖项.

我将举一个太复杂所以重构"案例的例子.

I'll give you an example for the "too-complicated-so-refactor" case.

我使用Zend_Session_Namespace"对象来存储模型的内部数据.该实例被注入到模型中,因此模拟不是问题.

I was using a "Zend_Session_Namespace" object for internal data storage of a model. That instance got injected into the model, so mocking was not an issue.

但是真正的命名空间"类的内部实现让我模拟了所有对 __set__get 的调用,它们在模型中的使用顺序是正确的.这很糟糕.因为每次我决定在我的代码中重新排序读取和写入值时,我都必须更改测试中的模拟,尽管没有任何问题.代码中的重构不应导致测试中断或强迫您更改它们.

But the internal implementation of the real "Namespace" class made me mock all the calls to __set and __get in the correct order of how they were used in the model. And that sucked. Because every time I decided to reorder the reading and writing of a value in my code, I had to change the mocking in the tests, although nothing was broken. Refactoring in the code should not lead to broken tests or force you to change them.

重构添加了一个新对象,将Zend_Session_Namespace"与模型分开.我创建了一个扩展ArrayObject"并包含命名空间"的对象.在创建时,所有值都从 Namespace 中读取并添加到 ArrayObject,并且在每次写入时,该值也会传递给 Namespace 对象.

The refactoring added a new object that separates the "Zend_Session_Namespace" from the model. I created an object that extends "ArrayObject" and contains the "Namespace". On creation, all the values got read from the Namespace and added to the ArrayObject, and on every write, the value also gets passed to the Namespace object as well.

我现在的情况是,我可以在所有测试中使用真正的扩展 ArrayObject,它本身只需要一个未配置的Zend_Session_Namespace"模拟实例,因为我不需要测试值是否正确存储在当我测试模型时的会话.我只需要一个在模型内部使用的数据存储.

I now had the situation that I could use a real extended ArrayObject for all my tests, which in itself only needed an unconfigured mocked instance of "Zend_Session_Namespace", because I did not need to test whether the values were correctly stored in the session when I tested the model. I only needed a data store that gets used inside the model.

为了测试会话是否被正确读取和写入,我对该 ArrayObject 本身进行了测试.

To test that the session gets correctly read and written, I have tests for that ArrayObject itself.

所以最后我使用了模型的真实实例,数据存储的真实实例以及Zend_Session_Namespace"的模拟实例,它什么都不做.我特意选择了将之前混入模型类的模型素材"和会话保存素材"分开——>单一职责原则".

So in the end I am using a real instance of the model, and a real instance of the data store together with a mocked instance of "Zend_Session_Namespace" which does nothing. I deliberately chose to separate "model stuff" and "session save stuff" which had been mixed into the model class before -> "single responsibility principle".

测试真的变得更容易了.而且我想说这也是一种代码异味:如果创建和配置模拟类很复杂,或者在更改测试类时需要进行大量更改,那么是时候考虑重构了.那里有问题.

The testing really got easier that way. And I'd say that this is also a code smell: If creating and configuring the mock classes is complicated, or needs a lot of changes when you change the tested class, it is time to think about refactoring. There is something wrong there.

这篇关于单元测试 (PHP):何时伪造/模拟依赖项,何时不的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆