gpt4 book ai didi

php - 单元测试中的依赖

转载 作者:行者123 更新时间:2023-11-28 20:04:09 25 4
gpt4 key购买 nike

目前,我第一次尝试将单元测试应用于一个项目。出现两个问题:

  1. 如果多个测试相互依赖,这是不好的做法吗?在下面的代码中,几个测试需要其他测试的结果为正,这是一般的最佳实践吗?

  2. 您对 SUT 所依赖的模拟对象了解多少?在下面的代码中,“Router”依赖于“Route”,而“Route”又依赖于“RouteParameter”。 mock 还是不 mock ?

下面的代码是为了测试我的“路由器”对象,它通过 Router::addRoute($route) 接受路由,并通过 Router::route($url) 路由 URL

class RouterTest extends PHPUnit_Framework_TestCase {
protected function createSimpleRoute() {
$route = new \TNT\Core\Models\Route();
$route->alias = 'alias';
$route->route = 'route';
$route->parameters = array();

return $route;
}

protected function createAlphanumericRoute() {
$route = new \TNT\Core\Models\Route();
$route->alias = 'alias';
$route->route = 'test/[id]-[name]';

$parameterId = new \TNT\Core\Models\RouteParameter();
$parameterId->alias = 'id';
$parameterId->expression = '[0-9]+';

$parameterName = new \TNT\Core\Models\RouteParameter();
$parameterName->alias = 'name';
$parameterName->expression = '[a-zA-Z0-9-]+';

$route->parameters = array($parameterId, $parameterName);

return $route;
}

public function testFilledAfterAdd() {
$router = new \TNT\Core\Helpers\Router();

$router->addRoute($this->createSimpleRoute());

$routes = $router->getAllRoutes();

$this->assertEquals(count($routes), 1);

$this->assertEquals($routes[0], $this->createSimpleRoute());

return $router;
}

/**
* @depends testFilledAfterAdd
*/
public function testOverwriteExistingRoute($router) {
$router->addRoute(clone $this->createSimpleRoute());

$this->assertEquals(count($router->getAllRoutes()), 1);
}

/**
* @depends testFilledAfterAdd
*/
public function testSimpleRouting($router) {
$this->assertEquals($router->route('route'), $this->createSimpleRoute());
}

/**
* @depends testFilledAfterAdd
*/
public function testAlphanumericRouting($router) {
$router->addRoute($this->createAlphanumericRoute());

$found = $router->route('test/123-Blaat-and-Blaat');

$data = array('id' => 123, 'name' => 'Blaat-and-Blaat');

$this->assertEquals($found->data, $data);
}

/**
* @expectedException TNT\Core\Exceptions\RouteNotFoundException
*/
public function testNonExistingRoute() {
$router = new \TNT\Core\Helpers\Router();

$router->route('not_a_route');
}
}

最佳答案

1) 是的,如果测试相互依赖,那绝对是一种不好的做法。

单元测试的构建方式应使其在失败时立即指向代码中的特定区域。好的单元测试会减少调试的时间。如果测试相互依赖,您将失去这个好处,因为您无法判断代码中的哪个错误导致测试失败。此外,这是一个维护噩梦。如果您的“共享测试”发生变化怎么办,那么您将不得不更改所有依赖的测试。

Here你可以找到一些关于如何解决交互测试问题的很好的指导(整本 xUnit 测试模式书是必读的!)

2) 单元测试就是测试尽可能小的东西。

假设您有一个闹钟(C# 代码):

public class AlarmClock
{
public AlarmClock()
{
SatelliteSyncService = new SatelliteSyncService();
HardwareClient = new HardwareClient();
}

public void Execute()
{
HardwareClient.DisplayTime = SatelliteSyncService.GetTime();

// Check for active alarms
// ....
}
}

这是不可测试的。您将需要一个真正的卫星连接和一个硬件客户端来检查是否设置了正确的时间。

然而,下面的内容将让您同时模拟 hardwareClient 和 satelliteSyncService。

public AlarmClock(IHardwareClient hardwareClient, ISatelliteSyncService satelliteSyncService)
{
SatelliteSyncService = satelliteSyncService;
HardwareClient = hardwareClient;
}

然而,您永远不应该模拟您实际正在测试的对象(听起来合乎逻辑,但有时我看到它正在发生)。

那么,您应该在模拟方面走多远。您应该模拟测试所依赖的类的所有内容。通过这种方式,您可以完全隔离地测试您的类(class)。您还可以控制依赖项的结果,从而确保您的 SUT 将通过所有代码路径。

例如,让 SatelliteSyncService 抛出一个异常,让它返回一个无效的时间,当然让它返回正确的时间,然后在特定的时刻,这样你就可以测试你的闹钟是否被激活合适的时机。

用于创建您的路由测试数据。考虑使用 Builder Pattern.这将帮助您仅设置测试成功所需的条件。它将使您的测试更具表现力并且更容易被其他人阅读。它还会减少您的测试维护,因为您的依赖项更少。

我写了一个blog post关于扩展此处提到的想法的单元测试。它使用 C# 来解释概念,但它适用于所有语言。

关于php - 单元测试中的依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8908305/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com