文章转发自专业的Laravel开发者社区,原文链接:https://learnku.com/laravel/t...在这篇文章中,我将展示一个使用HTTP实例的测试中间件。HTTP级别的测试更能适应变化,可读性更强。在AdamWathan和TaylorOtwell最近的《全栈广播》(http://www.fullstackradio.com/72)节目中,听到他们在HTTP测试中发现了很多实用价值令人耳目一新。我发现HTTP测试更容易编写和维护,但我确实觉得我在测试Wrong?,或者通过不模拟(对象)和隔离每个测试项来作弊。如果您还没有听过这一集,它充满了实用的测试建议。简介今年早些时候,我在我的一个项目上构建了一个用于验证和保护Mailgunwebhook的中间件,并在LaravelNews描述的Laravel中使用Mailgun进行入站电子邮件处理中对此进行了介绍。总之,我将演示如何在处理入站电子邮件时使用Laravel中间件对Mailgunwebhook进行身份验证(以确保webhook实际上来自Mailgun)。在设置Mailgunwebhook的核心部分时,建议使用rquest提供的签名、时间戳和令牌来验证作为HTTPPOST负载一部分的签名,从而保护您的webhook。这是我发布的完整中间件:isMethod('post')){abort(Response::HTTP_FORBIDDEN,'只允许POST请求。');}if($this->verify($request)){返回$next($request);}abort(Response::HTTP_FORBIDDEN,'Webhook签名无效。');}protectedfunctionbuildSignature($request){returnhash_hmac('sha256',sprintf('%s%s',$request->input('timestamp'),$request->input('token')),config('服务.mailgun.secret'));}protectedfunctionverify($request){if(abs(time()-$request->input('timestamp'))>15){returnfalse;}返回$this->buildSignature($request)===$request->input('signature');这个中间件简单地接受一个POST请求并将传入的签名与使用Mailgun密钥生成的签名进行比较作为直接运行的中间件。在本文中,我将向您展示如何使用更高级别的HTTP测试来测试这个中间件。您的整个堆栈将在测试中运行,让您更有信心您的应用程序将按预期工作。不直接将测试绑定到特定的中间件实现这是您可以了解的一个重要好处。我们可以完全重构中间件,而无需更改任何测试或更新模拟来验证中间件是否正常工作。我相信您会发现这些测试会更加可靠。配置让我们使用示例Laravel5.5项目快速构建上述中间件的测试:$laravelnewmiddleware-tests#切换到middleware-tests文件夹$cd$_$phpartisanmake:middlewareValidateMailgunWebhook获取上面的中间件代码并粘贴它进入这个中间件文件。接下来,将此中间件添加到您的app/Http/Kernel.php文件中:编写HTTP测试我们将针对这个中间件编写一些测试,我们甚至不必定义任何routes/api.php来测试它!首先,让我们创建功能测试文件:$phpartisanmake:testSecureMailgunWebhookTest查看Mailgun中间件,这是我们要测试的内容,以确保中间件按预期工作:除POST之外的任何HTTP动词都应导致403Forbidden回复。无效签名应创建403Forbidden响应。一个有效的签名应该通过并命中可调用的路由。旧时间戳应导致403Forbidden响应。测试无效的HTTP方法有了这个介绍,让我们编写我们的第一个测试并设置我们的测试。使用以下内容更新SecureMailgunWebhookTest文件:设置('services.mailgun.secret','秘密');\Route::middleware('mailgun.webhook')->any('/_test/webhook',function(){return'OK';});}/**@test*/publicfunctionit_forbids_non_post_methods(){$this->withoutExceptionHandling();$异常计数=0;$httpVerbs=['get','put','patch','delete'];foreach($httpVerbsas$httpVerb){尝试{$response=$this->$httpVerb('/_test/webhook');}catch(HttpException$e){$exceptionCount++;$this->assertEquals(响应::HTTP_FORBIDDEN,$e->getStatusCode());$this->assertEquals('只允许POST请求。',$e->getMessage());}}if(count($httpVerbs)===$exceptionCount){返回;}$this->fail('预期403禁止');在setUp()方法中,我们定义了一个假的Mailgun键,这样我们就可以针对这个键编写测试,然后使用any()路由方法为我们的路由定义一个全局(包罗万象)路由将允许我们使用假测试路由以使用中间件发出HTTP请求。在Laravel5.5中引入了withoutExceptionHandling()方法,这意味着我们可以在测试中自己捕获抛出的异常,而不是使用HTTP响应来显示异常。try/catch将确保为每个HTTP请求捕获一个HttpException,并且还将提供一个递增的异常计数器。如果捕获到的异常数量与我们测试的HTTP请求数量相匹配,则测试通过。否则,如果我们的请求没有引发异常,$this->fail()方法将被调用。与使用注释相比,我更喜欢捕获和断言异常的方法。这让我感觉更清晰了,我也可以在异常上进行断言,确保异常是我所期望的。您可以使用以下PhpUnit命令直接运行中间件功能测试::#运行文件中的所有测试$./vendor/bin/phpunittests/Feature/SecureMailgunWebhookTest.php#过滤特定方法$./vendor/bin/phpunit\tests/Feature/SecureMailgunWebhookTest.php\--filter=it_forbids_non_post_methods测试无效签名下一个测试验证无效签名是否会导致403Forbidden错误。本次测试与第一个测试不同,它使用了POST方法,但是发送了无效的请求数据:/**@test*/publicfunctionit_aborts_with_an_invalid_signature(){$this->withoutExceptionHandling();尝试{$this->post('/_test/webhook',['timestamp'=>abs(time()-100),'token'=>'invalid-token','signature'=>'invalid-signature',]);}catch(HttpException$e){$this->assertEquals(Response::HTTP_FORBIDDEN,$e->getStatusCode());$this->assertEquals('Webhook签名无效。',$e->getMessage());返回;}$this->fail('Expectedthewebhooksignaturetobeinvalid.');}我们传递将导致签名无效的虚假数据,然后断言在HttpException中设置了正确的响应状态和消息。测试有效签名当webhook发送有效签名时,路由器将在不破坏中间件的情况下处理响应。如果签名匹配,中间件调用verify()然后调用$next():if($this->verify($request)){return$next($request);}签名、时间戳和令牌。我们将在测试类中构建SHA-256哈希版本,这几乎是中间件中相同方法的副本。中间件和我们的测试都将使用setup()方法中配置的services.mailgun.secret秘密:/**@test*/publicfunctionit_passes_with_a_valid_signature(){$this->withoutExceptionHandling();$时间戳=时间();$token='令牌';$response=$this->post('/_test/webhook',['timestamp'=>$timestamp,'token'=>$token,'signature'=>$this->buildSignature($timestamp,$token),]);$this->assertEquals('OK',$response->getContent());}保护函数buildSignature($timestamp,$token){returnhash_hmac('sha256',sprintf('%s%s',$timestamp,$token),config('services.mailgun.secret'));}我们的测试在中间件中使用相同的代码构建签名,因此我们可以生成中间件预期的有效签名。在测试结束时,我们断言测试路径中返回的响应内容等于“OK”。我们的中间件采取的另一个预防措施是,如果应用的时间戳是旧的,则不允许请求继续。该测试类似于断言失败的其他测试,但这次我们使除时间戳之外的所有内容都有效(签名和令牌):/**@test*/publicfunctionit_fails_with_an_old_timestamp(){try{$this->withoutExceptionHandling();$timestamp=abs(时间()-16);$token='令牌';$response=$this->post('/_test/webhook',['timestamp'=>$timestamp,'token'=>$token,'signature'=>$this->buildSignature($timestamp,$token),]);}catch(HttpException$e){$this->assertEquals(Response::HTTP_FORBIDDEN,$e->getStatusCode());$this->assertEquals('Webhook签名无效。',$e->getMessage());返回;}$this->fail('Thetimestampshouldhavefailedverification.');}注意$timestamp=abs(time()-16);这将使中间件时间戳比较无效。了解更多这是在HTTP级别测试中间件的快速呈现。我更喜欢这种级别的测试,因为在中间件上使用模拟(假数据)可能很乏味并且与特定的实现相关联。如果我选择稍后重构,我的测试很可能需要重写以匹配新的中间件。通过HTTP测试,我可以自由地重构中间件并期望得到相同的结果。在Laravel中编写[HTTP测试](https://laravel.com/docs/5.5/…)非常简单,我发现自己在这个级别做了更多的测试。我相信我写的测试很容易理解,因为我们不模拟任何东西。你应该熟练使用Laravel测试套件的断言(特性)进行测试。这些工具使您的测试工作更轻松,我敢说更有趣。如果您不熟悉测试,您还可以在Laravel新闻中查看TestDrivenLaravel。我也经历过这个;如果您刚刚开始测试Web应用程序,这是一个很好的资源。
