首先,我会说我来自Java世界(这很重要,真的)。
我已经编写了一段时间的PHP,我遇到的一个问题是由于缺少编译,有时可能在编译时很容易检测到错误(例如,给定的参数数量错误功能),可以默默传递。
通过添加单元测试,可以轻松检测到代码覆盖率增加。问题是,例如测试构造函数以检查传递的参数是否正确是否有意义?我不是指参数的数量,而是指这些参数的内容(例如,如果参数为null,某些对象应该启动异常以避免创建“脏”对象)。
问题是,我是否因多年的Java代码而受到污染?毕竟,增加代码覆盖率以“发现”错过的函数感觉就像(真正)原始的编译方式。
另外,我想说明我已经使用了开发环境(PHPStorm),我们也使用了像PHPCodeSniffer这样的工具。
有任何想法/建议吗?
答案 0 :(得分:4)
这是一个很好的问题,可以在多个层面上得到解答:
<强> 1。语言特征 正如您所指出的,PHP语言的特征与Java等强类型语言明显不同。这引发了一个严重的问题,即来自Java和C#等强类型语言的程序员可能不会意识到PHP行为的含义(例如你所描述的那些)。这引入了程序员错误的可能性(例如,程序员可能不太小心使用Java,因为他们知道编译器会捕获不正确的参数,但在PHP开发时可能不会应用适当的注意事项)。 因此,需要更好的程序员教育/监督来解决这个问题(例如内部公司编码标准,结对编程,代码审查)。它(正如你所指出的那样)提出了一个问题,即是否应该增加测试覆盖率以检查编译器会捕获的错误。
<强> 2。测试覆盖率 测试覆盖范围的参数是特定于项目的。在现实世界中,测试覆盖率的水平主要取决于客户的容错能力(由系统中发生的错误的后果决定)。如果您正在开发在实时控制系统上运行的软件,那么显然您将测试更多。在您的问题中,您将PHP视为首选语言;这同样适用于关键系统基础架构中不断增加的基于Web的前端数量。另一方面,如果您正在为模型铁路俱乐部开发一个简单的网站,并且正在开发一个时事通讯应用程序,那么您的客户可能不关心构造函数中是否存在错误。
第3。案例工具 最终,需要一种可以检测这些错误的CASE工具,例如缺少参数。如果那里没有合适的工具,为什么不创建自己的工具呢?大多数程序员都无法创建CASE工具,特别是如果您可以使用您的语言的开源解析引擎。如果你是开源倾向,这可能是一个很好的项目,或者你的公司可能会推出这样的解决方案。
<强>结论强> 在你的情况下,是否测试构造函数基本上归结为一个问题:我的系统失败的后果是什么?如果在测试构造函数上花费额外资源以避免此类失败具有经济意义,那么您应该这样做。否则,可能会通过较少的测试来完成,例如结对编程或代码审查。
答案 1 :(得分:3)
如果设置了无效参数,您是否希望构造函数抛出异常?你希望明天,下周和明年的行为方式相同吗?然后你编写一个测试来验证它确实存在。
测试验证您的代码是否符合您的要求。失败的无效参数是代码行为,与计算销售税或显示用户的个人资料页面一样多。
答案 2 :(得分:1)
我们测试构造函数,参数的顺序,未提供的默认值,然后是一些实际设置。例如:
class UTIL_CATEGORY_SCOPE extends UTIL_DEPARTMENT_SCOPE
{
function __construct($CategoryNo = NULL, $CategoryName = NULL)
{
parent::__construct(); // Do Not Pass fields to ensure that the array is checked when all fields are defined.
$this->DeclareClassFields_();
$this->CategoryName = $CategoryName;
$this->CategoryNo = $CategoryNo;
}
private function DeclareClassFields_()
{
$this->Fields['CategoryNo'] = new UTIL_ICAP_FIELD_PAIR_FIRST('CCL', 6, ML('Category'), 8);
$this->Fields['CategoryName'] = new UTIL_ICAP_FIELD_PAIR_SECOND('CCL', 32, ML('Name'), 15, array(), array(), NULL, UTIL_ICAP_FIELD::EDIT_DENY, UTIL_ICAP_FIELD::UPDATE_DENY, 'DES');
}
}
然后我们创建测试,不仅检查构造函数及其顺序,而且该类和继承没有改变。
public function testObjectCreation()
{
$CategoryInfo = new UTIL_CATEGORY_SCOPE();
$this->assertInstanceOf('UTIL_CATEGORY_SCOPE', $CategoryInfo);
$this->assertInstanceOf('UTIL_DEPARTMENT_SCOPE', $CategoryInfo);
$this->assertInstanceOf('UTIL_DATA_STRUCTURE', $CategoryInfo); // Inherited from UTIL_DEPARTMENT_SCOPE
}
public function testConstructFieldOrder()
{
$CategoryInfo = new UTIL_CATEGORY_SCOPE(1500, 'Category Name');
$this->assertEquals(1500, $CategoryInfo->CategoryNo);
$this->assertEquals('Category Name', $CategoryInfo->CategoryName);
}
public function testConstructDefaults()
{
$CategoryInfo = new UTIL_CATEGORY_SCOPE();
$this->assertNull($CategoryInfo->CategoryNo);
$this->assertNull($CategoryInfo->CategoryName);
}
public function testFieldsCreated()
{
$CategoryInfo = new UTIL_CATEGORY_SCOPE();
$this->assertArrayHasKey('CategoryNo', $CategoryInfo->Fields);
$this->assertArrayHasKey('CategoryName', $CategoryInfo->Fields);
$this->assertArrayHasKey('DeptNo', $CategoryInfo->Fields); // Inherited from Parent
$this->assertArrayHasKey('DeptName', $CategoryInfo->Fields); // Inherited from Parent
}