单元测试的艺术——入门篇

发表于:2017-8-01 11:33

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Helius-黑牛    来源:个人博客

  四、更多NUnit属性的介绍
  4.1、参数化测试
  NUnit有个很酷的功能,叫做参数化测试。可以从现有的测试中任意选择一个,进行一下修改:
  (1)把属性[Test]替换成属性[TestCase]
  (2)把测试中用到的硬编码的值替换成这个测试方法的参数
  (3)把替换掉的值放在属性的括号中[TestCase(param1,param2,...)]
  [TestCase("filewithbadextension.SLF")]
  [TestCase("filewithbadextension.slf")]
  public void IsValidLogFileName_ValidExtensions_ReturnsTrue(string file)
  {
  LogAnalyzer analyzer=new LogAnalyzer();
  bool result = analyzer.IsValidLogFileName(file);
  Assert.True(result);
  }
  需要注意的是:这个时候你需要用一个比较通用的名字重新命令这个测试方法。
  当然,[TestCase("")]不仅仅只可以写一个参数,也可以写N个参数。
  [TestCase("filewithbadextension.SLF",true)]
  [TestCase("filewithbadextension.slf",true)]
  public void IsValidLogFileName_ValidExtensions_ReturnsTrue(string file,bool excepted)
  {
  LogAnalyzer analyzer = new LogAnalyzer();
  bool result = analyzer.IsValidLogFileName(file);
  Assert.AreEqual(excepted,result);
  }
  4.2、[Setup]与[TearDown]
  进行单元测试时,很重要的一点是保证之前测试的遗留数据或者实例得到销毁,新测试的状态是重建的。幸好,NUnit有一些特别的属性,可以很方便地控制测试前后的设置和清理状态工作,就是[SetUp]和[TearDown]动作属性。
  [SetUp] NUnit每次在运行测试类里的任何一个测试时都会先运行这个方法
  [TearDown] 这个属性标识一个方法应该在测试类里的每个测试运行之后执行。
  private LogAnalyzer _logAnalyzer = null;
  [SetUp]
  public void Setup()
  {
  _logAnalyzer=new LogAnalyzer();
  }
  [Test]
  public void IsValidFileName_validFileLowerCased_ReturnsTrue()
  {
  bool result = _logAnalyzer.IsValidLogFileName("hello.slf");
  Assert.IsTrue(result,"filename should be valid!");
  }
  [Test]
  public void IsValidFileName_validFileUpperCased_ReturnsTrue()
  {
  bool result = _logAnalyzer.IsValidLogFileName("hello.SLF");
  Assert.IsTrue(result, "filename should be valid!");
  }
  [TearDown]
  public void TearDown()
  {
  _logAnalyzer = null;
  }
  虽然SetUp与TearDown用起来很方便,但是不建议使用,因为这种方式随着代码的增加,后面测试方法很快就变得难以阅读了,最好是采用工厂方法来初始化被测试的实例。
  4.3、检验预期的异常
  我们现在修改一下要测试的代码,在输入为Null或者Empty的时候,就跑出一个异常。
  public class LogAnalyzer
  {
  public bool IsValidLogFileName(string fileName)
  {
  if(string.IsNullOrEmpty(fileName))
  {
  throw new ArgumentException("filename has to be provided");
  }
  if (fileName.EndsWith(".SLF"))
  {
  return false;
  }
  return true;
  }
  测试代码如下:
  [Test]
  [ExpectedException(typeof (ArgumentException), ExceptedMessage = "fileName has to be provided")]
  public void IsValidFileName_EmptyFileName_ThrowsException()
  {
  MakeLogAnalyzer().IsValidLogFileName(string.Empty);
  }
  private LogAnalyzer MakeLogAnalyzer()
  {
  return new LogAnalyzer();
  }
  注意:以上的代码虽然是正确的,但是在NUint3.0中已经弃用了,原因是采用这种方法,你可能不知道哪一行代码抛出的这个异常,如果你的构造函数有问题,也抛出这个异常,那你所写的测试也会通过,但事实上是错误的。NUint提供了一个新的API,Assert.Catch<T>(delegate)。以下是使用Assert.Catch编写的测试代码:
  [Test]
  public void IsValidFileName_EmptyFileName_ThrowsException()
  {
  var ex = Assert.Catch<ArgumentException>(() => { MakeLogAnalyzer().IsValidLogFileName(""); });
  StringAssert.Contains("fileName has to be provided",ex.Message);
  }
  private LogAnalyzer MakeLogAnalyzer()
  {
  return new LogAnalyzer();
  }
  4.4、忽略测试
  有时候代码有问题,但是你又需要把代码签入到主代码中(这种情况应该是少中极少,因为这是一种错误的方式)。可以采用[Ignore]属性。示例如下:
  [Test]
  [Ignore("it has some problems")]
  public void IsValidFileName_validFileUpperCased_ReturnsTrue()
  {
  bool result = MakeLogAnalyzer().IsValidLogFileName("hello.SLF");
  Assert.IsTrue(result, "filename should be valid!");
  }
  结果如下:
  4.5、设置测试的类型
  可以把测试按指定的测试类别运行,例如:慢测试和快测试。使用[Category]属性可以实现这个功能。
  [Test]
  [Category("Fast Tests")]
  public void IsValidFileName_ValidFile_ReturnTrue()
  {
  Assert.IsTrue(MakeLogAnalyzer().IsValidLogFileName("xxx.SLF"));
  }
  4.6、测试系统状态的改变而非返回值
  上面所有测试示例,都是有根据被测试方法的返回值来进行测试,但一个工程里面不可能每个方法都是有返回值的,有的是需要判断系统状态的改变的,称为基于状态的测试。
  定义:通过检查被测试系统及其协作方(依赖物)在被测试方法执行后行为的改变,判定被测试方法是否正确工作。
  //被测试代码
  public class LogAnalyzer
  {
  public bool WasLastFileNameValid { get; set; }
  public bool IsValidLogFileName(string fileName)
  {
  WasLastFileNameValid = false;
  if (string.IsNullOrEmpty(fileName))
  {
  throw new ArgumentException("fileName has to be provided");
  }
  if (!fileName.EndsWith(".SLF"))
  {
  return false;
  }
  WasLastFileNameValid = true;
  return true;
  }
  }
  测试代码:
  [TestCase("filewithbadextension.SLF", true)]
  [TestCase("filewithbadextension.slf", true)]
  public void IsValidLogFileName_ValidExtensions_ReturnsTrue(string file, bool excepted)
  {
  LogAnalyzer analyzer = MakeLogAnalyzer();
  analyzer.IsValidLogFileName(file);
  Assert.AreEqual(excepted, analyzer.WasLastFileNameValid);
  }
  private LogAnalyzer MakeLogAnalyzer()
  {
  return new LogAnalyzer();
  }
  五、总结
  创建测试类、项目和方法的管理;
  测试命名要有规范;
  使用工厂方法重用测试中的代码,例如用来创建和初始化所有测试都要用到的对象代码;
  尽量不要使用[SetUp]和[TearDown],因为它们使测试变得难以理解。
33/3<123
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号