class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
}
}
所以如果它抛出异常就很容易测试
/**
* @expectedException Exception
*/
public function test()
{
new Testme(1);
}
但如果没有做任何事情怎么办?
public function test()
{
new Testme(2);
?? ? ? ? ?
}
答案 0 :(得分:9)
对于函数什么都不做,您有两种可能的方案:
您的功能不执行任何操作,因为您没有在其中执行操作,而不包含return
关键字:
public function doNothing()
{
// Do nothing.
}
您的功能不执行任何操作,因为您没有在其中执行操作,并且您确实包含其中的return
关键字没有表示任何返回值:
public function doNothing()
{
// Do nothing.
return;
}
我将不再处理以下情况:
除了执行可以在其他对象上测试的重要操作之外,您不返回任何内容的情况。在这种情况下,您必须对修改对象的结果状态进行单元测试。
除了返回某些内容之外什么都不做的情况,那么你应该对返回值进行单元测试。
对于第一种情况,PHP手册记录了函数的求值表达式为null
。它在这里说:http://php.net/manual/en/functions.returning-values.php在一个注释中:
如果省略返回值,则返回NULL值。
对于第二种情况,PHP手册记录了funcion的计算表达式也将是null
。它在这里说:http://php.net/manual/en/function.return.php在一个注释中:
如果未提供参数,则必须省略括号并返回NULL。 [...]
因此,有明确记载了一项“什么都不做”的功能。必须评估为null
。
坚持你的期望:
$this->assertNull( $sut->doNothing() );
通过这种方式锻炼"你的功能,你运行它使代码覆盖完成所有行,你"期望" "什么都没发生"通过测试其评估的null
值作为表达式,如文档所述。
然而,要测试一个构造函数......那么......常识:构造函数的目的是什么?创建某种类型的类,对吧?
所以...我更喜欢通过检查已创建$sut
来启动100%的单元测试。这是我在做新课时所做的第一次测试。这是我在课程存在之前编写的测试。最后,这就是构造函数的用途。红酒吧。然后我创建了这个类。绿色酒吧。
假设我有一个Email
类,它接受一个字符串,只有在传递有效的电子邮件时才会创建,否则会抛出异常。这与你的问题非常相似。一个只允许创建的构造函数"或者"通过爆炸系统来否认它"。
我通常会这样做:
//-------------------------------------------------//
// Tests //
//-------------------------------------------------//
/** @dataProvider validEmailProvider **/
public function testCreationIsOfProperClass( string $email )
{
$sut = $this->getSut( $validEmail );
$this->assertInstanceOf( Email::class, $sut );
}
/** @dataProvider invalidEmailProvider **/
public function testCreationThrowsExceptionIfEmailIsInvalid( string $invalidEmail )
{
$this->expectException( EmailException::class );
$this->getSut( $invalidEmail );
}
//-------------------------------------------------//
// Data providers //
//-------------------------------------------------//
public function validEmailProvider() : array
{
return
[
[ 'alice@example.com' ],
[ 'bob.with-several+symbols@subdomain.another.subdomain.example.verylongTLD' ],
]
}
public function invalidEmailProvider() : array
{
return
[
[ 'missing_at_symbol' ],
[ 'charlie@cannotBeOnlyTld' ],
]
}
//-------------------------------------------------//
// Sut creators //
//-------------------------------------------------//
private function getSut( string $email ) : Email
{
return new Email( $email );
}
当我使用PHP 7.0并且我将类型放在任何地方时,无论是输入参数还是返回类型,如果创建的对象不是电子邮件,getSut()函数将首先失败。
但是,即使我写了省略返回类型,测试也会测试预期会发生什么:new Email( 'valid@example.com' );
本身就是一个表达式,它会评估为"某些东西"班级Email::class
。
代码味道。构造函数可能不应该工作。如果有的话,只需存储参数。如果构造函数"确实有效"除了存储参数之外,还要考虑在getter上进行延迟处理,或者在工厂中委派该工作。
就像之前一样,然后获取数据。
希望这会有所帮助。
答案 1 :(得分:3)
这是不可能的。添加return
语句并断言结果。
class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
return true;
}
}
然后
$object = new Testme();
$this->assertTrue($object->testMe(2));
答案 2 :(得分:3)
在PHPUnit 7.2+中,您也可以使用TestCase::expectNotToPerformAssertions()
public function test()
{
// ...
$this->expectNotToPerformAssertions();
}
这与@doesNotPerformAssertions
批注具有相同的行为。
答案 3 :(得分:1)
注意:此解决方案的积分转到this related answer。上下文可能看起来有点不同,但解决方案/解决方法的工作方式相同。测试不抛出异常与测试没有返回值的方法是一样的。
根据this issue thread,没有内置的解决方案来测试PHPUnit中的DoesNotThrowException
之类的东西。
所以是的,一个解决方案是从你的方法返回一些虚拟值,比如
public function testMe ($a)
{
if ($a == 1) { throw new Exception ('YAY'); }
return true;
}
然后在测试中断言它。但是,如果您不想仅为测试更改代码,则可以解决此问题:
public function testExceptionIsNotThrown()
{
try {
new Testme(2);
}
catch(Exception $e) {
/* An exception was thrown unexpectedly, so fail the test */
$this->fail();
}
/* No exception was thrown, so just make a dummy assertion to pass the test */
$this->assertTrue(true);
}
它可能看起来很黑,而且不是很直观,但是if it's stupid but it works, it's not stupid。
答案 4 :(得分:1)
我遇到了同样的问题。确保"没有"已经发生了它,只需要在你的单元测试中称呼你的方法。如果失败,测试无论如何都会失败。
如果您只是在没有@expectedException
注释的情况下调用您的方法
public function test()
{
new Testme(1);
}
您将收到错误
There was 1 error:
1) Testme::testMe
Exception: YAY
答案 5 :(得分:1)
如今,最佳实践是完全针对以下情况进行注释:
/**
* @doesNotPerformAssertions
*/
public function testSomething()
{
$someService = new SomeObject();
$someService->shallNotFail();
}
答案 6 :(得分:0)
这是一个非常有趣的问题,尽管编写了很多答案,但是似乎没有一个人能够正确回答这个问题,因为您使用类要求过,让我以此方式进行解释。
请记住,您在类中创建的实例方法应该只有两个意图。
class Foo {
private $prop = null;
public function fooMethod() {
$this->prop = "string";
}
public function getProp() {
return $this->prop;
}
}
方法fooMethod()不会返回任何东西,但是会影响类中$ prop属性的状态,您可以通过
测试该方法$this->assertNotNull( $instance->getProp() );
因为您知道是否运行了此方法,所以应该影响prop $ prop并更改该变量的状态。
然后该方法为static
。它不应该是实例方法,静态方法通常具有返回类型,因为它们既不能影响类的状态,也不能返回状态变量。这限制了静态方法将结果存储在某个地方(除非将它们存储在全局变量中,否则不要这样做),因此,它肯定会返回一些输出。如果不想返回输出,则可以考虑从静态方法返回布尔值。