pierstoval / smoke-testing
Symfony应用程序的烟雾测试自动化工具
Requires
- php: ^8.1
- symfony/framework-bundle: ^6.1|^7.0
Requires (Dev)
- phpunit/phpunit: ^9.6|^10.0
- symfony/maker-bundle: ^1.43
README
一个极小的库,旨在简化为您的Symfony应用程序编写简单测试。
特别适用于大型未经测试的Symfony遗留项目。
这个库最初是受到@SmaineDev的一条推文的启发,您也可以在他的Github (@ismail1432)上找到他。
为什么要进行烟雾测试
烟雾测试是一种快速确认您的项目是否适合进行单元或功能测试的方法。
进行应用程序的烟雾测试时,通常需要执行应用程序的许多部分,只需确保它不会抛出错误即可。
打个比方,就像启动汽车引擎来确保它能够启动并且不会冒烟或爆炸(因此称为“烟雾”测试😉),但不会逐个测试雨刮器、制动器或变速器。
在Web开发的世界里,烟雾测试通常涉及到打开网站的所有页面,并确保它们不会返回HTTP服务器错误(状态码 > 500),有时还会测试404或其他HTTP客户端错误代码。
烟雾测试的另一个非常重要的用途是当您正在处理没有测试和/或没有文档的大型遗留项目时。
运行基本的烟雾测试(例如使用此库)成本非常低,并且可以检查整个项目的平均健康状况。
此外,添加手动测试(请参阅手动测试路由部分)可以使您对测试拥有更多的控制和更高级的设置(例如添加HTTP头,对页面内容进行预期等)。
安装
composer require --dev pierstoval/smoke-testing
使用方法
首先,您需要为您的应用程序配置PHPUnit(请参阅Symfony文档中的测试部分)。
现在您有三个选择来测试您的项目
🌊 测试所有路由
- 创建一个测试用例。
- 让它扩展
SmokeTestStaticRoutes
。 - 运行PHPUnit。
示例
<?php namespace App\Tests; use Pierstoval\SmokeTesting\SmokeTestStaticRoutes; class AllRoutesTest extends SmokeTestStaticRoutes { // That's all! }
就是这样!
您只需在项目中设置一次。
它做了什么?
SmokeTestStaticRoutes
类已经包含一个PHPUnit测试,该测试将使用Symfony Router通过找到应用程序的所有静态路由,并使用Symfony的HTTP Client对每个路由运行单个HTTP请求。
如果请求返回HTTP状态码 >= 500,则测试将失败。
否则,即使HTTP 400或404,测试也将成功。
注意:此类还将查找您的非静态路由!对于所有动态参数都具有defaults
的路由将被包含在烟雾测试套件中,尽可能最大化可测试路由的数量。
什么是静态路由?
简要总结,/api/endpoint
是一个静态路由,因为它没有动态参数。
另一方面,/{_locale}/posts
不是静态的,因为它包含一个动态参数{_locale}
。如果你的路由包含所有
{...}
动态参数的defaults
,这意味着Symfony Router可以生成URL而无需传递这些参数,因此可以通过此包进行烟测试。
如简介所述,4** HTTP状态码可能是完全正常和预期的,例如,当你的应用程序的某些部分依赖于身份验证时。
个人建议,你应在项目中至少为400/401/403状态码创建测试。大多数测试都是基于需要验证的客户输入或身份验证,这些是应用程序的关键入口点,因此必须进行彻底测试。
自定义SmokeTestStaticRoutes
的使用
为了能够自定义HTTP测试,例如,当你有很多路由且许多路由都在身份验证下时,你可以使用SmokeTestStaticRoutes
类中可用的扩展点
beforeRequest(KernelBrowser $client, string $routeName, string $routePath)
允许你在发送HTTP请求到后端之前进行挂钩。这是你调用应用程序后端并创建要添加到HTTP客户端对象的身份验证令牌的地方。
由于提供了路由名称和路径,你甚至可以根据路由名称或路径进行过滤(例如,只为以/api/
开头的路由添加身份验证)。afterRequest(KernelBrowser $client, string $routeName, string $routePath)
允许你在发送HTTP请求后立即挂钩,在断言非500类HTTP状态码之前。afterAssertion(KernelBrowser $client, string $routeName, string $routePath)
允许你在发送HTTP请求到后端后立即添加更多断言,以防你需要添加更多断言。
如果你实现这些方法(在类中默认为空),你必须将它们至少实现为protected
,并且非静态。
建议
根据Symfony最佳实践,所有路由都应配置HTTP方法(GET、POST等)。
如果你的路由没有按照这种方式配置,RoutesExtractor
(由SmokeTestStaticRoutes
类使用)将触发一个E_USER_DEPRECATED
错误。
如果您不希望触发弃用,您可以按这种方式自定义SMOKE_TESTING_ROUTES_METHODS
环境变量的内容
自定义SMOKE_TESTING_ROUTES_METHODS
环境变量
🔬手动进行烟测试路由
除了(或与)检查您应用程序的所有路由之外,您还可以运行您选择的URL列表,并控制所有请求参数和测试断言/期望。
这是我推荐使用烟测试的温和而优雅的开始的方法,因为这种方法为您提供了对测试的很大控制权,允许您创建比简单的烟测试更全面的测试(最终更像功能测试)。
- 将
FunctionalSmokeTester
特质添加到您的类中。 - 使用
FunctionalTestData
类创建功能测试数据(见以下示例)。 - 在测试用例中执行
$this->runFunctionalTest
方法,将您的FunctionalTestData
实例作为第一个参数。 - 运行PHPUnit。
示例
<?php namespace App\Tests; use Pierstoval\SmokeTesting\FunctionalSmokeTester; use Pierstoval\SmokeTesting\FunctionalTestData; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class AllRoutesTest extends WebTestCase { use FunctionalSmokeTester; // Allows using the "$this->runFunctionalTest()" method. public function testGet200(): void { $this->runFunctionalTest( FunctionalTestData::withUrl('/my-route') ->expectRouteName('my_successful_route') ->expectStatusCode(200) ->expectTextToBePresent('Hello world!') ); } }
为了方便,您还可以创建一个data provider
来仅执行所需的URL
<?php namespace App\Tests; use Pierstoval\SmokeTesting\FunctionalSmokeTester; use Pierstoval\SmokeTesting\FunctionalTestData; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class AllRoutesTest extends WebTestCase { use FunctionalSmokeTester; /** * @dataProvider provideTestUrls */ public function testMyUrls(FunctionalTestData $testData): void { $this->runFunctionalTest($testData); } public function provideTestUrls(): \Generator { yield '/my-route' => FunctionalTestData::withUrl('/my-route') ->expectRouteName('my_successful_route') ->expectStatusCode(200) ->expectTextToBePresent('Hello world!'); yield '/my-other-route' => FunctionalTestData::withUrl('/my-other-route') ->withHttpHeader('Authorization', 'Bearer 2d5b0cfb531745668') ->expectRouteName('my_other_route') ->expectStatusCode(200) ->expectTextToBePresent('{"message": "Bonjour!"}'); } }
在这里解释了FunctionalTestData
类的整个API
🔎带有可能粒度定制的烟测试所有路由
要求
ℹ 注意:此功能需要
symfony/maker-bundle
。
如果您使用symfony/flex
,则自动安装SmokeTestingBundle
。
如果没有,您可以将它添加到您的config/bundles.php
文件中
<?php // config/bundles.php return [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true, 'test' => true], // ... // Add this line ⬇ Pierstoval\SmokeTesting\SmokeTestingBundle::class => ['dev' => true, 'test' => true], ];
使用方法
运行bin/console make:smoke-tests
命令。
这将创建一个包含所有生成的测试的tests/FunctionalTest.php
文件。
生成的测试将具有与在SmokeTestStaticRoutes
类中使用的测试完全相同的行为
,但它们将不是自动生成和使用,而是将写入你的测试文件中。
它们将使用FunctionalTestData
类和$this->runFunctionalTest()
方法,就像上面的方法一样。
如果你想要一次性生成测试并不再使用FunctionalTestData
,可以在make:smoke-tests
命令中添加--no-dto
选项:它将使用Symfony的经典请求系统创建常规测试。
示例
- 使用此库的DTO对象
<?php namespace App\Tests; use Pierstoval\SmokeTesting\FunctionalSmokeTester; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class FunctionalTest extends WebTestCase { use FunctionalSmokeTester; public function testRouteGet200WithMethodGet(): void { $this->runFunctionalTest( FunctionalTestData::withUrl('/200') ->withMethod('GET') ->expectRouteName('get_200') ->appendCallableExpectation($this->assertStatusCodeLessThan500('GET', '/200')) ); } // ... }
- 使用Symfony的原生功能测试
<?php namespace App\Tests; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class FunctionalTest extends WebTestCase { public function testRouteGet200WithMethodGet(): void { $client = static::createClient(); $crawler = $client->request('GET', '/'); static::assertLessThan( 500, $client->getResponse()->getStatusCode(), 'Request "GET /200" for route "get_200" returned an internal error.', ); } // ... }
这种方法有优点也有缺点
- 优点
- 你可以使用
--no-dto
一次性生成所有测试,并如果你想的话移除库 - 与任何基于Symfony的测试套件兼容,只要你有HTTP路由。
- 你仍然测试了所有你的路由
- 如果你的项目中某些功能是关键性的,你可以专注于特定的HTTP路由,复制/粘贴初始代码并为这个特定功能创建更多测试:代码已经在那里帮助你!它仍然测试其他路由。
- 你可以使用
- 缺点
- 如果你有很多路由,这可能会创建一个很大的文件
- 新路由不会自动添加,你需要自己添加