如何对这样的东西进行单元测试?
How to unit test something like this?
我有一个这样的例子,它基于 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 注释或其他东西忽略它?
干杯。
如果 100% 的覆盖率是您的目标,那么您将需要为此继续执行可能 "over optimising" 您的代码的路线。在这种情况下,它不会受到太大的打击。
一个选项是将加密行抽象到它自己的部分可模拟方法中,但是这样做(并告诉 PHPUnit 它会做什么 return)你实际上只是在检查是否可以抛出异常(字面意思)。
从语义上讲,getter/setter 方法往往是 public - 虽然没有任何机构严格执行。我完全可以理解为什么您不希望 getPrivateKey
作为 API 的一部分,但是您 可以 切实可行地向 setPrivateKey
添加一个方法到您的 public API - 这将解决您的单元测试问题:
# 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();
}
当然 - 通过这样做你 运行 进入 "you should not test code you don't own" 的论点,即 openssl
方法。这是一个 trade-off 如果这是您的目标,您将实现 100% 的覆盖率。
您需要将 openssl_public_encrypt
存根为 return false。
假设您的 generateRandomKey
方法属于 class 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();
}
}
我有一个这样的例子,它基于 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 注释或其他东西忽略它?
干杯。
如果 100% 的覆盖率是您的目标,那么您将需要为此继续执行可能 "over optimising" 您的代码的路线。在这种情况下,它不会受到太大的打击。
一个选项是将加密行抽象到它自己的部分可模拟方法中,但是这样做(并告诉 PHPUnit 它会做什么 return)你实际上只是在检查是否可以抛出异常(字面意思)。
从语义上讲,getter/setter 方法往往是 public - 虽然没有任何机构严格执行。我完全可以理解为什么您不希望 getPrivateKey
作为 API 的一部分,但是您 可以 切实可行地向 setPrivateKey
添加一个方法到您的 public API - 这将解决您的单元测试问题:
# 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();
}
当然 - 通过这样做你 运行 进入 "you should not test code you don't own" 的论点,即 openssl
方法。这是一个 trade-off 如果这是您的目标,您将实现 100% 的覆盖率。
您需要将 openssl_public_encrypt
存根为 return false。
假设您的 generateRandomKey
方法属于 class 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();
}
}