我有一个基于Laravel密钥生成器的示例:
public function generateRandomKey(): string
{
$generatedKey = base64_encode(random_bytes(16));
// Encrypt the generated key with our public key so it is not a 'plain password' anymore.
$value = openssl_public_encrypt($generatedKey, $crypted, $this->getPublicKey());
if (!$value) {
throw new \RuntimeException('Encryption failed: could not generate a random key.');
}
return base64_encode($crypted);
}
我想对此进行单元测试,我可以期待RuntimeException
。我希望100%的代码覆盖率,但我也不想强迫方案只是为了获得100%的代码覆盖率。
在这个例子中,我也希望PHPUnit能够触及异常。我无法提供错误的密钥,因为我的getPublicKey()
是私有的,并且在我遇到此方法中的错误之前会抛出错误。
触发解密错误并不难,因为我只能提供一个未正确加密的随机值。
那么我如何能够测试这样的场景并实现100%的代码覆盖率。测试这样的东西是可能的,甚至是明智的,还是应该用PHPUnit注释或其他什么来忽略它?
干杯。
答案 0 :(得分:1)
如果100%的覆盖率是您的目标,那么您将需要沿着潜在的路线前进,而不是优化"你的代码是为了它。在这种情况下,它不会太受欢迎。
一种选择是将加密线抽象为它自己的部分可模拟方法,但是通过这样做(并告诉PHPUnit它将返回什么),你基本上只是检查可以抛出异常(字面意思)。
从语义上讲,getter / setter方法往往是公开的 - 而这并不是由任何机构严格执行的。我完全可以理解为什么您不希望getPrivateKey
作为API的一部分,但可以可行地向您的公共API添加setPrivateKey
方法 - 这将解决你的单元测试问题:
# File: YourClass
public function setPrivateKey(string $key) : YourClass
{
$this->privateKey = $key;
return $this;
}
然后:
# File: YourClassTest
/**
* @expectedException RuntimeException
* @expectedExceptionMessage Encryption failed: could not generate a random key.
*/
public function testGenerateRandomKeyThrowsExceptionWhenCannotEncrypt()
{
$class = new YourClass;
$class->setPrivateKey('highly-unlikely-to-be-a-valid-private-key');
$class->generateRandomKey();
}
当然 - 通过这样做,您会遇到这样的争论:"您不应该测试您不拥有的代码,即openssl
方法。如果这是您的目标,那么您可以通过权衡取得100%的覆盖率。
答案 1 :(得分:0)
您需要将openssl_public_encrypt
存根以返回false。
假设您的generateRandomKey
方法属于类StephanV
,则测试可能如下所示:
namespace Tests;
function openssl_public_encrypt()
{
return false;
}
class StephanVTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \RuntimeException
*/
public function testGenerateRandomKeyThrowsException()
{
$cut = new StephanV;
$cut->generateRandomKey();
}
}