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);
?? ? ? ? ?
}
场景
函数不执行任何操作有两种可能的情况:
场景 1:无返回语句
您的函数不执行任何操作,因为您不在其中执行操作,并且您不包含 return
关键字:
public function doNothing()
{
// Do nothing.
}
场景 2:使用 return 语句
您的函数不执行任何操作,因为您不在其中执行操作,并且在其中包含 return
关键字而不表示任何返回值:
public function doNothing()
{
// Do nothing.
return;
}
其他方案
我将省略这些情况来处理以下情况:
您不返回任何内容,但执行可在其他对象上测试的重要操作的情况。在这种情况下,必须对已修改对象的结果状态进行单元测试。
如果您只返回某些内容,则应该对返回值进行单元测试。
浏览 PHP 手册中的文档
对于第一种情况,PHP 手册记录了函数的计算表达式将被null
。它在这里说: http://php.net/manual/en/functions.returning-values.php 在注释中
如果省略返回值,则将返回值 NULL。
对于第二种情况,PHP 手册记录了函数的计算表达式也将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 上进行延迟处理,或者在工厂左右委派该工作。
如何测试"只存储参数"的构造函数
就像之前+然后获取数据一样。
- 在第一次测试中测试创建是否是某物的实例。
- 然后在另一个不同的测试中,练习类似 getter 的东西,即使构造函数没有做任何事情(除了存储它(,它也会让你在构造函数中输入什么。
希望这有帮助。
在 PHPUnit 7.2+ 中,你也可以使用 TestCase::expectNotToPerformAssertions()
public function test()
{
// ...
$this->expectNotToPerformAssertions();
}
这与@doesNotPerformAssertions
批注具有相同的行为。
> 2018+
如今,最佳实践是完全针对这些情况进行注释:
/**
* @doesNotPerformAssertions
*/
public function testSomething()
{
$someService = new SomeObject();
$someService->shallNotFail();
}
- 拉取请求示例
- PHPUnit 文档(差(
是不可能的。添加return
语句并断言结果。
class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
return true;
}
}
然后
$object = new Testme();
$this->assertTrue($object->testMe(2));
注意:此解决方案的学分将转到此相关答案。上下文可能看起来有点不同,但解决方案/解决方法的工作方式相同。测试是否引发异常与测试没有返回值的方法相同。
根据此问题线程,还没有内置的解决方案来测试 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);
}
它可能看起来很笨拙,不是很直观,但如果它很愚蠢,但它有效,它并不愚蠢。
public function testThrowingException()
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('YAY');
(new Testme())->testMe(1);
}
public function testNotThrowingException()
{
$this->expectNotToPerformAssertions();
(new Testme())->testMe(2);
}
这是一个非常有趣的问题,虽然写了很多答案,但似乎没有一个正确回答了这个问题,因为你已经用类问了,让我解释一下。
请记住,您在类中创建的实例方法应该只有 2 个意图。
- 它可以改变类的状态(像私有变量一样改变类的属性(
- 它返回类的状态(getters(
除非它是静态方法,否则任何其他事物都是没有意义的。 例如如果你有这样的课程
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
.它不应该是一个实例方法,静态方法通常具有返回类型,因为它们不能影响类的状态,也不能返回状态变量。这限制了静态方法将结果存储在某处(除非您将它们存储为全局变量,否则不要这样做(,因此它肯定会返回一些输出。如果您不想返回输出,则可以考虑从静态方法返回布尔值。
我偶然发现了同样的问题。为了确保"什么都没有"发生,只需在单元测试中调用该方法就足够了。如果失败,测试无论如何都会失败。
如果你只是调用你的方法而不使用这样的@expectedException
注释
public function test()
{
new Testme(1);
}
您会收到错误
There was 1 error:
1) Testme::testMe
Exception: YAY