网站优化的目的营销型网站建设论坛
2026/4/18 12:43:20 网站建设 项目流程
网站优化的目的,营销型网站建设论坛,重庆购物网站建设,主机一键wordpress一个测试用例#xff0c;而不是一个测试断言。 断言轮盘并不意味着多重断言就是坏事。当我指导团队或单个开发人员进行测试驱动开发#xff08;TDD#xff09;或单元测试时#xff0c;经常会遇到一种特别的观念#xff1a; 多个断言是不好的。一个测试必须只有一个断言。…一个测试用例而不是一个测试断言。断言轮盘并不意味着多重断言就是坏事。当我指导团队或单个开发人员进行测试驱动开发TDD或单元测试时经常会遇到一种特别的观念 多个断言是不好的。一个测试必须只有一个断言。这种想法很少有用。让我们看一个实际的代码示例然后来试着理解这种观念的起源。由外至内的 TDD考虑使用 REST API 进行和取消餐厅预订。首先通过 HTTP POST 请求进行预订POST /restaurants/1/reservations?sigepi301tdlc57d0HwLCz[...] HTTP/1.1Content-Type: application/json{at:2023-09-22 18:47,name:Teri Bell,email:terribleexample.org,quantity:1}HTTP/1.1201CreatedContent-Type: application/json; charsetutf-8Location:/restaurants/1/reservations/971167d4c79441b78fe70cc702[...]{id:971167d4c79441b78fe70cc702d3e1f6,at:2023-09-22T18:47:00.0000000,email:terribleexample.org,name:Teri Bell,quantity:1}请注意在适当的 REST 方式下响应会在 Location 标头中返回已创建预订的位置。如果你改变主意了可以通过 DELETE 请求取消预订DELETE /restaurants/1/reservations/971167d4c79441b78fe70cc702[...] HTTP/1.1HTTP/1.1200 OK假设这就是我们想要的交互。使用由外至内的TDD编写如下测试:[Theory][InlineData(884,18,47,cexample.net,Nick Klimenko,2)][InlineData(902,18,50,emotexample.gov,Emma Otting,5)]public async TaskDeleteReservation(int days,int hours,int minutes,string email,string name,int quantity){usingvar api newLegacyApi();var at DateTime.Today.AddDays(days).At(hours, minutes).ToIso8601DateTimeString();var dto Create.ReservationDto(at, email, name, quantity);var postResp await api.PostReservation(dto);Uri address FindReservationAddress(postResp);var deleteResp await api.CreateClient().DeleteAsync(address);Assert.True(deleteResp.IsSuccessStatusCode,$Actual status code: {deleteResp.StatusCode}.);}这个例子是在c#中使用xUnit.net因为我们需要一些语言和框架来展示真实的代码。不过本文的观点适用于各种语言和框架。本文中的代码示例基于我的著作《Code That Fits in Your Head》中的示例代码库。为了通过这个测试你可以像这样实现服务器端代码[HttpDelete(restaurants/{restaurantId}/reservations/{id})]publicvoidDelete(int restaurantId,string id){}虽然这显然是一个空操作但它通过了所有测试。新编写的测试断言 HTTP 响应会返回 200成功范围内的状态代码。这是 API 的 REST 协议的一部分因此该响应非常重要。你希望保留此断言作为回归测试。如果 API 开始返回 400 或 500 范围内的状态代码这将是一个重大变化。到目前为止一切顺利。TDD 是一个渐进的过程。一个测试并不能驱动一个完整的功能。既然所有测试都通过了你就可以将更改提交到源代码控制中然后进行下一次迭代。加强后置条件你应该能够通过发起一个GET请求来检查资源是否真的消失了:GET /restaurants/1/reservations/971167d4c79441b78fe70cc702[...] HTTP/1.1HTTP/1.1404NotFound然而这并不是 Delete 当前实现的行为它什么也没做。这样看来你需要再做一次测试。有一种方法是复制现有测试并更改断言阶段执行上述 GET 请求以检查响应状态是否为 404[Theory][InlineData(884,18,47,cexample.net,Nick Klimenko,2)][InlineData(902,18,50,emotexample.gov,Emma Otting,5)]public async TaskDeleteReservationActuallyDeletes(int days,int hours,int minutes,string email,string name,int quantity){usingvar api newLegacyApi();var at DateTime.Today.AddDays(days).At(hours, minutes).ToIso8601DateTimeString();var dto Create.ReservationDto(at, email, name, quantity);var postResp await api.PostReservation(dto);Uri address FindReservationAddress(postResp);var deleteResp await api.CreateClient().DeleteAsync(address);var getResp await api.CreateClient().GetAsync(address);Assert.Equal(HttpStatusCode.NotFound, getResp.StatusCode);}这个方法确实可以提示你正确地实现服务器端Delete方法。但这真的是一个好主意吗使用这个方法测试代码是否易于维护呢测试代码也是代码你必须维护它。在测试代码中复制和粘贴会造成问题原因与在生产代码中复制和粘贴会造成问题的原因相同。如果以后要修改某些内容你必须确定所有需要编辑的地方。生产代码很容易遗漏掉某一处从而导致错误。测试代码亦是如此。一个操作更多断言与其复制粘贴第一个测试为什么不加强第一个测试用例的后置条件呢只需在第一个断言后添加新的断言即可[Theory][InlineData(884,18,47,cexample.net,Nick Klimenko,2)][InlineData(902,18,50,emotexample.gov,Emma Otting,5)]public async TaskDeleteReservation(int days,int hours,int minutes,string email,string name,int quantity){usingvar api newLegacyApi();var at DateTime.Today.AddDays(days).At(hours, minutes).ToIso8601DateTimeString();var dto Create.ReservationDto(at, email, name, quantity);var postResp await api.PostReservation(dto);Uri address FindReservationAddress(postResp);var deleteResp await api.CreateClient().DeleteAsync(address);Assert.True(deleteResp.IsSuccessStatusCode,$Actual status code: {deleteResp.StatusCode}.);var getResp await api.CreateClient().GetAsync(address);Assert.Equal(HttpStatusCode.NotFound, getResp.StatusCode);}这意味着你只需要维护一个测试方法而不是两个几乎完全相同的重复方法。但是我指导过的一些人可能会说这个测试有两个断言的确如此。那又怎样这是一个测试用例 取消预订。虽然取消预订是一个单独的操作但我们关心的是多个结果DELETE 请求成功后的状态代码应在 200 范围内。预订资源应该消失了。在进一步开发系统的过程中我们可能会添加更多我们关心的行为。也许系统还应该发送一封关于取消预订的电子邮件。我们也应该断言这一点。不过这仍然是相同的测试用例: 成功取消预订。在一个测试中使用多个断言并没有什么问题。上面的例子说明了它的好处。一个测试用例可以有多个应该被验证的结果。单一断言概念的起源每次测试只有一个断言的概念从何而来我不知道但我可以猜测。优秀的《xUnit Test Patterns》一书中描述了一种名为 “断言轮盘”Assertion Roulette的测试气味。它描述了一种很难确定到底是哪个断言导致了测试失败的情况。在我看来每项测试只有一个断言的 “规则 “是对断言轮盘描述的误读造成的。(甚至我自己可能也有责任。我不记得我是否参与过。xUnit 测试模式描述了断言轮盘的两个原因急于测试 单个测试验证的功能过多。缺失断言信息。你可能正试图模拟一个 “会话”在这个会话中客户端会执行许多步骤来实现一个目标。正如 Gerard Meszaros 就测试气味所写的那样这适用于人工测试但很少用于自动化测试。导致问题的不是断言的数量而是测试做得太多。另一个原因是当断言非常相似时你无法判断哪一个失败了同时它们也没有断言信息。上面例子的情况并非如此。如果 Assert.True 断言失败断言信息会告诉你Actual status code:NotFound.Expected:TrueActual:False同样如果 Assert.Equal 断言失败也会一目了然Assert.Equal()FailureExpected:NotFoundActual: OK这里没有歧义。一次测试一个断言既然你已经明白了每个测试可以有多个断言那么你就可以无所顾忌地添加断言了。不过在通常情况下像 “一次测试一个断言 “这样根深蒂固的理念中也蕴含着真理的萌芽。所以需要我们进行正确的判断。如果你认真思索一下什么是自动化测试它基本上就是一个谓词。它是一种声明表明我们期待一种特定的结果。然后我们将实际结果与预期结果进行比较看两者是否相等。因此从本质上讲理想的断言是这样的Assert.Equal(expected, actual);我并不总能实现这一理想断言但只要能做到我就会感到非常满足。有时expected 和 actual 是原始值如整数或字符串但它们也可能是复杂值代表测试所关注的程序状态子集。只要对象在结构上相等这样的断言就是有意义的。有时我无法找到像这样简洁表达验证步骤的方法不得不再添加一两个断言时我就会这么做。总结有一种观点认为每个单元测试只能写一个断言。这可能是出于对错误测试代码的真正担忧但多年来”断言轮盘”Assertion Roulette这一微妙的测试气味已经变成了一种更简单、但不太有用的 “规则”。这个“规则”经常会阻碍测试代码的可维护性。遵循“规则”的程序员诉诸于无端的复制和粘贴而不是在现有测试中添加另一个断言。如果在现有测试中添加相关断言是最好的方法就不要让一个被误解的规则阻止你。感谢每一个认真阅读我文章的人礼尚往来总是要有的虽然不是什么很值钱的东西如果你用得到的话可以直接拿走这些资料对于【软件测试】的朋友来说应该是最全面最完整的备战仓库这个仓库也陪伴上万个测试工程师们走过最艰难的路程希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询