unit testing和静态方法
阅读并理解unit testing,试图理解下面的post ,解释静态函数调用的困难。
我不清楚这个问题。 我一直认为静态函数是在一个类中四舍五入实用函数的好方法。 例如,我经常使用静态函数来初始化,即:
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 ...
但是,我为这门课写的几十个testing是一样的。 我什么也没有改变,他们仍然通过。 难道我做错了什么?
该职位的作者陈述如下:
静态方法的基本问题是它们是程序代码。 我不知道如何对程序代码进行unit testing。 unit testing假定我可以孤立地实例化一个应用程序。 在实例化过程中,我使用mocks / friendlies替代真正的依赖关系。 通过程序编程,没有任何东西可以“连线”,因为没有对象,代码和数据是分开的。
现在,我从post中了解到,静态方法创build依赖关系,但不直观地掌握为什么不能像常规方法那样容易地testing静态方法的返回值?
我将避免使用静态方法,但是我希望有一个静态方法有用的概念。 从这篇文章看来,静态方法和全局variables一样邪恶,应该尽可能地避免。
任何额外的信息或主题的链接将不胜感激。
静态方法本身并不比实例方法更难testing。 当一个方法(静态的或者其他的)调用其他的静态方法时会产生麻烦,因为你不能隔离被testing的方法。 以下是一个可能难以testing的典型示例方法:
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 }
你想用这种方法testing什么?
- 传递除正整数以外的任何其他内容将引发
InvalidIdentifierException
。 -
Database::query()
收到正确的标识符。 - find匹配的用户时返回,否则返回
null
。
这些要求很简单,但您还必须设置日志logging,连接到数据库,加载数据等。 Database
类应独自负责testing它是否可以连接和查询。 Log
类应该做相同的日志logging。 findUser()
不应该处理任何这个,但它必须是因为它依赖于它们。
如果相反,上面的方法调用了Database
和Log
实例上的实例方法,则testing可以传递特定于testing的脚本化返回值的模拟对象。
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)); }
如果findUser()
忽略调用connect()
,为$id
(上面的5
)传递错误的值,或者返回非null
值,上述testing将失败。 其优点是不涉及数据库,使得testing快速而健壮,这意味着不会由于与networking故障或样本数据错误等testing无关的原因而失败。 它允许您关注真正重要的内容: findUser()
包含的function。
塞巴斯蒂安·伯格曼(Sebastian Bergmann)同意米斯科·赫维(Misko Hevery)的看法,
- Stubbing和Mocking静态方法
unit testing需要接缝,接缝是我们阻止执行正常代码path的地方,也是我们如何实现被testing类的隔离。 接缝通过多态性工作,我们重写/实现类/接口,然后以不同的方式连接被testing的类,以便控制执行stream。 有了静态方法,没有什么可以重载。 是的,静态方法很容易调用,但如果静态方法调用另一个静态方法,则无法覆盖调用的方法依赖关系。
静态方法的主要问题在于它们引入了耦合,通常是通过将依赖关系硬编码到消费代码中,使得在unit testing中很难将其replace为存根或模拟。 这违反了开放/封闭原则和依赖倒置原则 ,这是两个SOLID原则 。
你是绝对正确的, 静力学被认为是有害的 。 避免他们。
请检查链接的其他信息。
更新:请注意,尽pipe静态方法仍然被认为是有害的, 但从PHPUnit 4.0开始,已经删除了存根和模拟静态方法的function
在testing静态方法时,我没有看到任何问题(至less没有一个在非静态方法中不存在)。
- 模拟对象使用dependency injection传递给testing中的类。
- 模拟静态方法可以通过合适的自动加载器或者操作
include_path
传递给被testing的类。 - 后期的静态绑定处理在同一个类中调用静态方法的方法。