单元测试和静态方法
阅读并学习单元测试,尝试理解 下面的文章解释了静态函数调用的困难。
我不太清楚这个问题。我一直认为静态函数是在类中汇总实用函数的好方法。例如,我经常使用静态函数调用来初始化,即:
Init::loadConfig('settings.php');
Init::setErrorHandler(APP_MODE);
Init::loggingMode(APP_MODE);
// start loading app related objects ..
$app = new App();
// 读完这篇文章后,我现在的目标是这个......
$init = new Init();
$init->loadConfig('settings.php');
$init->loggingMode(APP_MODE);
// etc ...
但是,我为此类编写的几十个测试是相同的。我什么也没改变,它们仍然全部通过。我做错了什么吗?
该帖子的作者声明如下:
静态方法的基本问题是它们是过程代码。我不知道如何对程序代码进行单元测试。单元测试假设我可以单独实例化我的应用程序的一部分。在实例化过程中,我将依赖项与模拟/友好连接起来,以替换真正的依赖项。对于过程式编程,由于没有对象,因此无需“连接”任何内容,代码和数据是分开的。
现在,我从帖子中了解到静态方法创建依赖关系,但没有直观地理解为什么不能测试静态方法的返回值和常规方法一样容易吗?
我将避免静态方法,但我希望了解静态方法何时有用(如果有的话)。从这篇文章看来,静态方法与全局变量一样邪恶,应该尽可能避免。
有关该主题的任何其他信息或链接将不胜感激。
Reading up and picking up on unit testing, trying to make sense of the following post on that explains the hardships of static function calls.
I don't clearly understand this issue. I have always assumed static functions were a nice way of rounding up utility functions in a class. For example, I often use static functions calls to initialise, ie:
Init::loadConfig('settings.php');
Init::setErrorHandler(APP_MODE);
Init::loggingMode(APP_MODE);
// start loading app related objects ..
$app = new App();
// After reading the post, I now aim for this instead ...
$init = new Init();
$init->loadConfig('settings.php');
$init->loggingMode(APP_MODE);
// etc ...
But, the few dozen tests I had written for this class are the same. I changed nothing and they still all pass. Am I doing something wrong?
The author of the post states the following:
The basic issue with static methods is they are procedural code. I have no idea how to unit-test procedural code. Unit-testing assumes that I can instantiate a piece of my application in isolation. During the instantiation I wire the dependencies with mocks/friendlies which replace the real dependencies. With procedural programing there is nothing to “wire” since there are no objects, the code and data are separate.
Now, I understand from the post that static methods create dependencies, but don't grasp intuitively why one cannot test the return value of a static method just as easily as a regular method?
I will be avoiding static methods, but I would of liked having an idea of WHEN static methods are useful, if at all. It seems from this post static methods are just about as evil as global variables and should be avoided as much as possible.
Any additional information or links on the subject would be greatly appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
静态方法本身并不比实例方法更难测试。当方法(静态或其他方式)调用其他静态方法时,就会出现问题,因为您无法隔离正在测试的方法。以下是一个难以测试的典型示例方法:
您可能想用此方法测试什么?
InvalidIdentifierException
。Database::query()
收到正确的标识符。null
。这些要求很简单,但您还必须设置日志记录、连接到数据库、加载数据等。Database 类应该单独负责测试它是否可以连接和查询。
Log
类应该对日志记录执行相同的操作。findUser()
不应该处理任何这些,但它必须处理,因为它依赖于它们。相反,如果上面的方法调用
Database
和Log
实例上的实例方法,则测试可以传入具有特定于当前测试的脚本化返回值的模拟对象。如果
findUser()
忽略调用connect()
,为$id
传递错误的值 (5< /code> 上面),或者返回
null
以外的任何内容。优点在于不涉及数据库,使测试快速而稳健,这意味着它不会因与测试无关的原因(例如网络故障或错误的样本数据)而失败。它使您能够专注于真正重要的事情:findUser()
中包含的功能。Static methods themselves aren't harder to test than instance methods. The trouble arises when a method--static or otherwise--calls other static methods because you cannot isolate the method being tested. Here is a typical example method that can be difficult to test:
What might you want to test with this method?
InvalidIdentifierException
.Database::query()
receives the correct identifier.null
when not.These requirements are simple, but you must also setup logging, connect to a database, load it with data, etc. The
Database
class should be solely responsible for testing that it can connect and query. TheLog
class should do the same for logging.findUser()
should not have to deal with any of this, but it must because it depends on them.If instead the method above made calls to instance methods on
Database
andLog
instances, the test could pass in mock objects with scripted return values specific to the test at hand.The above test will fail if
findUser()
neglects to callconnect()
, passes the wrong value for$id
(5
above), or returns anything other thannull
. The beauty is that no database is involved, making the test quick and robust, meaning it won't fail for reasons unrelated to the test like network failure or bad sample data. It allows you to focus on what really matters: the functionality contained withinfindUser()
.Sebastian Bergmann 同意 Misko Hevery 的观点,并经常引用他的观点:
静态方法的主要问题是它们引入了耦合,通常是通过对依赖关系进行硬编码到您使用的代码中,使得在单元测试中很难用存根或模拟来替换它们。这违反了开放/封闭原则和依赖倒置原则,其中两个SOLID 原则。
静态被认为是有害的,您说得完全正确。避开他们。
请检查链接以获取更多信息。
更新:请注意,虽然静态仍然被认为是有害的,从 PHPUnit 4.0 开始,已删除存根和模拟静态方法的功能
Sebastian Bergmann agrees with Misko Hevery and quotes him frequently:
The main issue with static methods is that they introduce coupling, usually by hardcoding the dependency into your consuming code, making it difficult to replace them with stubs or mocks in your Unit-Tests. This violates the Open/Closed Principle and the Dependency Inversion Principle, two of the SOLID principles.
You are absolutely right that statics are considered harmful. Avoid them.
Check the links for additional information please.
Update: note that while statics are still considered harmful, the capability to stub and mock static methods has been removed as of PHPUnit 4.0
测试静态方法时我没有看到任何问题(至少在非静态方法中不存在任何问题)。
include_path
传递给被测类。I do not see any problem when testing static methods (at least none that doesn't exists in non-static methods).
include_path
.