php 单元测试和静态方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5961023/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
unit testing and Static methods
提问by stefgosselin
Reading up and picking up on unit testing, trying to make sense of the following poston 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.
任何有关该主题的其他信息或链接将不胜感激。
回答by David Harkness
Static methods themselves aren't harder to test than instance methods. The trouble arises when a method--static or otherwise--calls otherstatic methods because you cannot isolate the method being tested. Here is a typical example method that can be difficult to test:
静态方法本身并不比实例方法更难测试。当一个方法——静态的或其他的——调用其他静态方法时就会出现问题,因为你不能隔离被测试的方法。这是一个可能难以测试的典型示例方法:
public function findUser($id) {
Assert::validIdentifier($id);
Log::debug("Looking for user $id"); // writes to a file
Database::connect(); // needs user, password, database info and a database
return Database::query(...); // needs a user table with data
}
What might you want to test with this method?
您可能想用这种方法测试什么?
- Passing anything other than a positive integer throws
InvalidIdentifierException
. Database::query()
receives the correct identifier.- A matching User is returned when found,
null
when not.
- 传递正整数以外的任何东西都会抛出
InvalidIdentifierException
。 Database::query()
接收正确的标识符。- 找到
null
时返回匹配的用户,否则返回。
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. The Log
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.
这些要求很简单,但您还必须设置日志记录、连接到数据库、加载数据等。Database
该类应该单独负责测试它是否可以连接和查询。这个Log
类应该对日志做同样的事情。findUser()
不应该处理任何这些,但它必须,因为它取决于他们。
If instead the method above made calls to instance methods on Database
and Log
instances, the test could pass in mock objects with scripted return values specific to the test at hand.
相反,如果上述方法调用了实例方法Database
和Log
实例,则测试可以传入具有特定于手头测试的脚本返回值的模拟对象。
function testFindUserReturnsNullWhenNotFound() {
$log = $this->getMock('Log'); // ignore all logging calls
$database = $this->getMock('Database', array('connect', 'query');
$database->expects($this->once())->method('connect');
$database->expects($this->once())->method('query')
->with('<query string>', 5)
->will($this->returnValue(null));
$dao = new UserDao($log, $database);
self::assertNull($dao->findUser(5));
}
The above test will fail if findUser()
neglects to call connect()
, passes the wrong value for $id
(5
above), or returns anything other than null
. 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 within findUser()
.
如果findUser()
忽略调用connect()
、为$id
(5
上面)传递错误的值或返回除null
. 美妙之处在于不涉及数据库,使测试快速而健壮,这意味着它不会因与测试无关的原因而失败,例如网络故障或样本数据不良。它使您可以专注于真正重要的事情:包含在findUser()
.
回答by Gordon
Sebastian Bergmann agrees with Misko Hevery and quotes him frequently:
Sebastian Bergmann 同意 Misko Hevery 的观点并经常引用他的话:
Unit-Testing needs seams, seams is where we prevent the execution of normal code path and is how we achieve isolation of the class under test. Seams work through polymorphism, we override/implement class/interface and then wire the class under test differently in order to take control of the execution flow. With static methods there is nothing to override. Yes, static methods are easy to call, but if the static method calls another static method there is no way to override the called method dependency.
单元测试需要接缝,接缝是我们阻止正常代码路径执行的地方,也是我们如何实现被测类的隔离。接缝通过多态工作,我们覆盖/实现类/接口,然后以不同的方式连接被测类以控制执行流程。使用静态方法没有什么可以覆盖的。是的,静态方法很容易调用,但是如果静态方法调用另一个静态方法,则无法覆盖被调用的方法依赖项。
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 Principleand the Dependency Inversion Principle, two of the SOLID principles.
静态方法的主要问题是它们引入了耦合,通常是通过将依赖项硬编码到您的消费代码中,从而难以在您的单元测试中用存根或模拟来替换它们。这违反了开放/封闭原则和依赖倒置原则,这两个SOLID 原则。
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
更新:请注意,虽然静态仍然被认为是有害的,但从 PHPUnit 4.0 起,存根和模拟静态方法的功能已被删除
回答by Oswald
I do not see any problem when testing static methods (at least none that doesn't exists in non-static methods).
我在测试静态方法时没有发现任何问题(至少没有在非静态方法中不存在的问题)。
- Mock objects are passed to classes under test using dependency injection.
- Mock static methodscan be passed to classes under test using a suitable autoloader or manipulating the
include_path
. - Late static binding deals with methods calling static methods in the same class.
- 使用依赖注入将模拟对象传递给被测类。
- 模拟静态方法可以使用合适的自动装载器或操纵被传递给类被测
include_path
。 - 后期静态绑定处理调用同一类中的静态方法的方法。