鼎鼎知识库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

为什么测试这么重要

人非圣贤孰能无过,归根结底,代码是人写的,Bug终究是无法避免的。测试的目的是为了尽早发现问题,尽量减少Bug数量。

测试类型

Smoke test

  • 程序员自己测试

Unit Tests

  • 通常由程序员做
  • 快,几毫秒
  • 独立
  • 一次测试一种行为
  • 不依赖于外界,比如使用MoqnSubstitute

Integration Tests

  • 通常由程序员做
  • 相对较慢
  • 依赖于外部实现,比如依赖数据库、外部服务

Functional Tests

  • 从用户的角度测试功能,可能是内测人员
  • 可以手动测试,也有自动测试框架

Subcutaneous Tests

  • 最接近UI下的测试
  • 通常由程序员做
  • 适合逻辑在后端业务层,比如前后端分离的后端业务层

Load Tests

  • 通常由程序员做
  • 模拟程序的负载
  • 通常查看各项性能指标

Stress Tests

  • 通常由程序员做
  • 通常查看CPU,Network,Memory指标

t3

单元测试的要和不要

  • 要间接测试私有方法
  • 要符合条件/不符合条件的输入
  • 容易出问题的代码要单元测试,比如包含正则表达式
  • 很难被捕捉的异常要单元测试,比如路由、算法

不要

  • 不要100%的覆盖率,没太必要
  • 不要给依赖组件的运行时错误进行单元测试
  • 不要在数据库架构、外部服务方面进行单元测试
  • 性能测试不通过单元测试
  • 代码生成器生成的代码不需要单元测试
  • 如果测试代码远大于被测试代码不需要单元测试

一定要让测试不通过

不好的

[Test]
public void ShouldAddTwoNumbers()
{
   var calculator = new Calculator();
   var result = calculator.Sum(10, 11);
   Assert.Equal(21, result);
}

// The method to test in class Calculator ...
public int Sum(int x, int y)
{
   throw new NotImplementedException();
}

好的

[Test]
public void ShouldAddTwoNumbers()
{
   var calculator = new Calculator();
   var result = calculator.Sum(10, 11);
   Assert.Equal(21, result);
}

// The method to test in class Calculator ...
public int Sum(int x, int y)
{
   return 0;//返回一个值让不通过
}

通过单元测试消除Bug

如果通过单元测试发现一个Bug, 即单元测试显示红灯。重构代买,单元测试通过,显示绿色。于是,单元测试起到了帮助代码重构的作用。

流行的测试框架

  • NUnit: 是.NET开源的、被认可的测试框架,有UI
  • XUnit: 来之NUnit作者,最新的,鼓励TDD的开发方式,甚至.NET Core团队也在使用
  • MSTest: 微软的测试框架,无法在build serverCI/CD

持续集成服务器

监控源代码,一旦有变化,检查、构建、自动测试、发送报告等。

测试项目的文件结构

t4

命名

  • MethodName_StateUnderTest_ExpectedBehavior
isAdult_AgeLessThan18_False
withdrawMoney_InvalidAccount_ExceptionThrown
admitStudent_MissingMandatoryFields_FailToAdmit
  • MethodName_ExpectedBehavior_StateUnderTest
  • test[Feature being tested]
testIsNotAnAdultIfAgeLessThan18
testFailToWithdrawMoneyIfAccountIsInvalid
  • Feature to be tested
IsNotAnAdultIfAgeLessThan18
FailToWithdrawMoneyIfAccountIsInvalid
  • Should_ExpectedBehaviour_When_StateUnderTest
Should_ThrowException_When_AgeLessThan18
Should_FailToWithdrawMoney_ForInvalidAccount
  • When_StateUnderTest_Expect_ExpectedBehavior
When_AgeLessThan18_Expect_isAdultAsFalse
When_InvalidAccount_Expect_WithdrawMoneyToFail
  • Given_Preconditions_When_StateUnderTest_Then_ExpectedBehavior
Given_UserIsAuthenticated_When_InvalidAccountNumberIsUsedToWithdrawMoney_Then_TransactionsWillFail

AAA

[TestMethod]
public void TestRegisterPost_ValidUser_ReturnsRedirect()
{
   // Arrange
   AccountController controller = GetAccountController();
   RegisterModel model = new RegisterModel()
   {
      UserName = "someUser",
      Email = "goodEmail",
      Password = "goodPassword",
      ConfirmPassword = "goodPassword"
   };
   // Act
   ActionResult result = controller.Register(model);
   // Assert
   RedirectToRouteResult redirectResult = (RedirectToRouteResult)result;
   Assert.AreEqual("Home", redirectResult.RouteValues["controller"]);
   Assert.AreEqual("Index", redirectResult.RouteValues["action"]);
}

数据量很大很难测试时用单元测试

被测代码,数量越大越难测,很有必要使用单元测试。

public decimal CalculateTotal(List<myitem> items)
{
	decimal total = 0.0m;
	foreach(MyItem i in items)
	{
	 	total += i.UnitPrice * (i - i.Discount);
	}
	return total;
}

保持被测方法不要过于复杂

Test Explorer的使用

t5

t6

对被测代码复杂逻辑的封装

不好

while ((ActiveThreads > 0 || AssociationsQueued > 0) && (IsRegistered || report.TotalTargets <= 1000 )
&& (maxNumPagesToScan == -1 || report.TotalTargets < maxNumPagesToScan) && (!CancelScan))

好的

while (!HasFinishedInitializing (ActiveThreads, AssociationsQueued, IsRegistered,
report.TotalTargets, maxNumPagesToScan, CancelScan))

使用Selenium进行Web测试