- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我一直在研究如何将单元测试覆盖范围添加到用 PHP 编写的大型现有代码库中。静态类和可实例化类中的许多函数调用库或实例化对象以获得与内存缓存和数据库的连接。它们通常看起来像这样:
public function getSomeData() {
$key = "SomeMemcacheKey";
$cache = get_memcache();
$results = $cache->get($key);
if (!$results) {
$database = new DatabaseObject();
$sql = "SELECT * from someDatabase.someTable";
$results = $database->query($sql);
$cache->set($key, $results);
}
return $results;
}
我和我的同事目前正在尝试通过 PHPUnit 为我们正在编写的一些新类实现覆盖。我试图找到一种方法,以隔离的方式为我们现有代码库中类似于上述伪代码的函数创建单元测试,但没有成功。
我在 PHPUnit 文档中看到的示例都依赖于类中的一些方法,通过这些方法可以将模拟对象附加到它,例如:$objectBeingTested->attach($mockObject);
我查看了 SimpleUnit,在那里看到了同样的事情,模拟对象通过其构造函数传递到类中。这不会为实例化自己的数据库对象的函数留出太多空间。
有没有办法模拟这些类型的调用?我们可以使用另一个单元测试框架吗?或者我们是否必须更改我们在未来使用的模式以促进单元测试?
我想做的是能够在运行测试时用模拟类替换整个类。例如,DatabaseObject 类可以替换为模拟类,并且在测试期间实例化的任何时候,它实际上都是模拟版本的实例。
我的团队一直在谈论重构我们在新代码中访问数据库和内存缓存的方法,也许使用单例。我想如果我们以这样一种方式编写单例,它自己的实例可以用模拟对象替换,这可能会有所帮助......
这是我第一次涉足单元测试。如果我做错了,请这样说。 :)
谢谢。
最佳答案
只是添加到@Ezku 的答案(+1,我也会说的所有内容)到最终代码可能看起来像这样(使用 Dependency injection )
public function __construct(Memcached $mem, DatabaseObject $db) {
$this->mem = $mem;
$this->db = $db;
}
public function getSomeData() {
$key = "SomeMemcacheKey";
$cache = $this->mem;
$results = $cache->get($key);
if (!$results) {
$database = $this->db;
$sql = "SELECT * from someDatabase.someTable";
$results = $database->query($sql);
$cache->set($key, $results);
}
return $results;
}
有了它,创建模拟对象并将它们传递到代码中真的很容易。
您可能想要这样做的原因有很多(除了创建可测试的代码之外)。这一次它使您的代码更容易更改(想要不同的数据库?传入不同的数据库对象而不是更改 DatabaseObject 中的代码。
This Blog post告诉您为什么静态方法不好,但是在您的代码中使用“new”运算符与说 $x = StaticStuff::getObject();
几乎是一回事,所以它也适用于此。
另一个引用可以是:Why singletons are bad for testable code因为它涉及相同的点。
如果您已经编写了更多代码,则有一些方法可以在不立即更改所有内容的情况下实现这些想法。
像这样的可选依赖注入(inject):
public function __construct(Memcached $mem = null, DatabaseObject $db = null) {
if($mem === null) { $mem = new DefaultCacheStuff(); }
if($db === null) { $db = new DefaultDbStuff(); }
$this->mem = $mem;
$this->db = $db;
}
public function getSomeData() {
$key = "SomeMemcacheKey";
$cache = $this->mem;
$results = $cache->get($key);
if (!$results) {
$database = $this->db;
$sql = "SELECT * from someDatabase.someTable";
$results = $database->query($sql);
$cache->set($key, $results);
}
return $results;
}
或使用“setter 注入(inject)”:
public function __construct(Memcached $mem = null, DatabaseObject $db = null) {
$this->mem = new DefaultCacheStuff();
$this->db = new DefaultDbStuff();
}
public function setDatabaseObject(DatabaseObject $db) {
$this->db = $db;
}
public function setDatabaseObject(Memcached $mem) {
$this->mem = $mem;
}
public function getSomeData() {
$key = "SomeMemcacheKey";
$cache = $this->mem;
$results = $cache->get($key);
if (!$results) {
$database = $this->db;
$sql = "SELECT * from someDatabase.someTable";
$results = $database->query($sql);
$cache->set($key, $results);
}
return $results;
}
另外还有一些叫做 dependency injection containers
的东西可以让你把所有的反对意见都扔掉并将所有东西从那个容器中拉出来,但是因为它让测试有点困难(恕我直言)而且它只如果做得很好,对你有帮助我不建议从一个开始,而只是使用普通的“依赖注入(inject)”来创建可测试的代码。
关于php - 在实例化自己对象的函数内部使用 PHP 中的模拟对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5281418/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!