鼎鼎知识库
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

04单元测试基本面.md 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. # 为什么测试这么重要
  2. `人非圣贤孰能无过`,归根结底,代码是人写的,`Bug`终究是无法避免的。测试的目的是为了尽早发现问题,尽量减少`Bug`数量。
  3. # 测试类型
  4. ## `Smoke test`
  5. - 程序员自己测试
  6. ## `Unit Tests`
  7. - 通常由程序员做
  8. - 快,几毫秒
  9. - 独立
  10. - 一次测试一种行为
  11. - 不依赖于外界,比如使用`Moq`和`nSubstitute`
  12. ## `Integration Tests`
  13. - 通常由程序员做
  14. - 相对较慢
  15. - 依赖于外部实现,比如依赖数据库、外部服务
  16. ## `Functional Tests`
  17. - 从用户的角度测试功能,可能是内测人员
  18. - 可以手动测试,也有自动测试框架
  19. ## `Subcutaneous Tests`
  20. - 最接近`UI`下的测试
  21. - 通常由程序员做
  22. - 适合逻辑在后端业务层,比如前后端分离的后端业务层
  23. ## `Load Tests`
  24. - 通常由程序员做
  25. - 模拟程序的负载
  26. - 通常查看各项性能指标
  27. `Stress Tests`
  28. - 通常由程序员做
  29. - 通常查看`CPU`,`Network`,`Memory`指标
  30. ![t3](F:\SourceCodes\DDWiki\专题\后端\测试\t3.png)
  31. # 单元测试的要和不要
  32. - 要间接测试私有方法
  33. - 要符合条件/不符合条件的输入
  34. - 容易出问题的代码要单元测试,比如包含正则表达式
  35. - 很难被捕捉的异常要单元测试,比如路由、算法
  36. 不要
  37. - 不要100%的覆盖率,没太必要
  38. - 不要给依赖组件的运行时错误进行单元测试
  39. - 不要在数据库架构、外部服务方面进行单元测试
  40. - 性能测试不通过单元测试
  41. - 代码生成器生成的代码不需要单元测试
  42. - 如果测试代码远大于被测试代码不需要单元测试
  43. # 一定要让测试不通过
  44. 不好的
  45. ```
  46. [Test]
  47. public void ShouldAddTwoNumbers()
  48. {
  49. var calculator = new Calculator();
  50. var result = calculator.Sum(10, 11);
  51. Assert.Equal(21, result);
  52. }
  53. // The method to test in class Calculator ...
  54. public int Sum(int x, int y)
  55. {
  56. throw new NotImplementedException();
  57. }
  58. ```
  59. 好的
  60. ```
  61. [Test]
  62. public void ShouldAddTwoNumbers()
  63. {
  64. var calculator = new Calculator();
  65. var result = calculator.Sum(10, 11);
  66. Assert.Equal(21, result);
  67. }
  68. // The method to test in class Calculator ...
  69. public int Sum(int x, int y)
  70. {
  71. return 0;//返回一个值让不通过
  72. }
  73. ```
  74. # 通过单元测试消除`Bug`
  75. 如果通过单元测试发现一个`Bug`, 即单元测试显示红灯。重构代买,单元测试通过,显示绿色。于是,单元测试起到了帮助代码重构的作用。
  76. # 流行的测试框架
  77. - `NUnit`: 是`.NET`开源的、被认可的测试框架,有`UI`
  78. - `XUnit`: 来之`NUnit`作者,最新的,鼓励`TDD`的开发方式,甚至`.NET Core`团队也在使用
  79. - `MSTest`: 微软的测试框架,无法在`build server`跑`CI/CD`
  80. # 持续集成服务器
  81. 监控源代码,一旦有变化,检查、构建、自动测试、发送报告等。
  82. # 测试项目的文件结构
  83. ![t4](F:\SourceCodes\DDWiki\专题\后端\测试\t4.png)
  84. # 命名
  85. - `MethodName_StateUnderTest_ExpectedBehavior`
  86. ```
  87. isAdult_AgeLessThan18_False
  88. withdrawMoney_InvalidAccount_ExceptionThrown
  89. admitStudent_MissingMandatoryFields_FailToAdmit
  90. ```
  91. - `MethodName_ExpectedBehavior_StateUnderTest`
  92. - `test[Feature being tested]`
  93. ```
  94. testIsNotAnAdultIfAgeLessThan18
  95. testFailToWithdrawMoneyIfAccountIsInvalid
  96. ```
  97. - `Feature to be tested`
  98. ```
  99. IsNotAnAdultIfAgeLessThan18
  100. FailToWithdrawMoneyIfAccountIsInvalid
  101. ```
  102. - `Should_ExpectedBehaviour_When_StateUnderTest`
  103. ```
  104. Should_ThrowException_When_AgeLessThan18
  105. Should_FailToWithdrawMoney_ForInvalidAccount
  106. ```
  107. - `When_StateUnderTest_Expect_ExpectedBehavior`
  108. ```
  109. When_AgeLessThan18_Expect_isAdultAsFalse
  110. When_InvalidAccount_Expect_WithdrawMoneyToFail
  111. ```
  112. - `Given_Preconditions_When_StateUnderTest_Then_ExpectedBehavior`
  113. ```
  114. Given_UserIsAuthenticated_When_InvalidAccountNumberIsUsedToWithdrawMoney_Then_TransactionsWillFail
  115. ```
  116. # `AAA`
  117. ```
  118. [TestMethod]
  119. public void TestRegisterPost_ValidUser_ReturnsRedirect()
  120. {
  121. // Arrange
  122. AccountController controller = GetAccountController();
  123. RegisterModel model = new RegisterModel()
  124. {
  125. UserName = "someUser",
  126. Email = "goodEmail",
  127. Password = "goodPassword",
  128. ConfirmPassword = "goodPassword"
  129. };
  130. // Act
  131. ActionResult result = controller.Register(model);
  132. // Assert
  133. RedirectToRouteResult redirectResult = (RedirectToRouteResult)result;
  134. Assert.AreEqual("Home", redirectResult.RouteValues["controller"]);
  135. Assert.AreEqual("Index", redirectResult.RouteValues["action"]);
  136. }
  137. ```
  138. # 数据量很大很难测试时用单元测试
  139. 被测代码,数量越大越难测,很有必要使用单元测试。
  140. ```
  141. public decimal CalculateTotal(List<myitem> items)
  142. {
  143. decimal total = 0.0m;
  144. foreach(MyItem i in items)
  145. {
  146. total += i.UnitPrice * (i - i.Discount);
  147. }
  148. return total;
  149. }
  150. ```
  151. # 保持被测方法不要过于复杂
  152. # `Test Explorer`的使用
  153. ![t5](F:\SourceCodes\DDWiki\专题\后端\测试\t5.png)
  154. ![t6](F:\SourceCodes\DDWiki\专题\后端\测试\t6.png)
  155. # 对被测代码复杂逻辑的封装
  156. 不好
  157. ```
  158. while ((ActiveThreads > 0 || AssociationsQueued > 0) && (IsRegistered || report.TotalTargets <= 1000 )
  159. && (maxNumPagesToScan == -1 || report.TotalTargets < maxNumPagesToScan) && (!CancelScan))
  160. ```
  161. 好的
  162. ```
  163. while (!HasFinishedInitializing (ActiveThreads, AssociationsQueued, IsRegistered,
  164. report.TotalTargets, maxNumPagesToScan, CancelScan))
  165. ```
  166. # 使用`Selenium`进行`Web`测试