• 新手单元测试教程


    1.为什么需要单元测试?
    单元测试基于它可以: 减少程序潜在的问题; 程序即说明文档; 
    但它可能带来的一个问题是:你会觉得编写这些测试程序很费时间,但相对于软件后期的维护,你会发现这样做其实是值得的。

    先说介绍一下,Assert类所在的命名空间为
    Microsoft.VisualStudio.TestTools.UnitTesting 在工程文件中只要引用Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll就可以使用了

    1、 AreEqual:方法被重载了N多次,主要功能是判断两个值是否相等;如果两个值不相等,则测试失败。
    2、 AreNotEqual:方法被重载了N多次,主要功能是判断两个值是否不相等;如果两个值相等,则测试失败。
    3、 AreNotSame:引用的对象是否不相同;如果两个输入内容引用相同的对象,则测试失败.
    4、 AreSame:引用的对象是否相同;如果两个输入内容引用不相同的对象,则测试失败.
    5、 Fail:断言失败。
    6、 Inconclusive:表示无法证明为 true 或 false 的测试结果
    7、 IsFalse:指定的条件是否为 false;如果该条件为 true,则测试失败。
    8、 IsTrue:指定的条件是否为 true;如果该条件为 false,则测试失败
    9、 IsInstanceofType:测试指定的对象是否为所需类型的实例;如果所需的实例不在该对象的继承层次结构中,则测试失败
    10、 IsNotInstanceofType: 测试指定的对象是否为所需类型的实例;如果所需的实例在该对象的继承层次结构中,则测试失败
    11、 IsNull:测试指定的对象是否为非空
    12、 IsNotNull:测试指定的对象是否为非空

    主要针对public共有函数

    例子代码:


    public string GetName()
    {
    return "Hello World";
    }

    public int ToInt(string value)
    {
    int result = 0;
    if (!string.IsNullOrWhiteSpace(value))
    {
    if (!int.TryParse(value, out result))
    {
    throw new Exception("文本内容无法转换为Int类型。");
    }
    }
    else
    {
    throw new Exception("文本不能为空。");
    }
    return result;
    }

    测试代码:

    [TestMethod]
    public void GetName()
    {
    HelloWorld helloworld = new HelloWorld();
    var name = helloworld.GetName();
    Assert.AreEqual(name, "Hello World");
    }

    [TestMethod]
    public void ToInt()
    {
    HelloWorld helloworld = new HelloWorld();
    string value = "5";
    int expected = 5;//预期的值
    int actual;//实际的值
    actual = helloworld.ToInt(value);
    Assert.AreEqual(expected, actual);
    value = "5.5";
    expected = 5;
    actual = helloworld.ToInt(value);
    Assert.AreEqual(expected, actual);
    }



    2

    一个共有的 Public 方法实现某一主要功能,但是由于该功能的实现非常复杂,需要很多的辅助类,辅助方法。由于代码封装性的需求,我们通常需要把这些辅助的类方法定义为非Public,静态static的(非必须,但是静态方法会提升性能),如 private, internal 等。

    但是这也带来了一个问题,如何对这些非 public 的类,方法进行单元测,毕竟这些才是完成逻辑的代码?

    PrivateObject and PrivateType

    PrivateObject主要测试私有函数

    PrivateType主要测试私有static 函数

    主要用这个两个类

    例子代码:

    private static Double Format(Double fileSize)
    {
    Double size = fileSize;
    size = size / 1024 / 1024;
    size = (int)(size * 10 + 0.5) / 10.0;
    return size;
    }

    private int Add(int x,int y)
    {
    return x + y;
    }

    单元测试代码:

    [TestMethod]
    public void Format()
    {
    PrivateType pt = new PrivateType(typeof(Medium));
    var value = pt.InvokeStatic("Format", 4.3);
    Assert.AreEqual(value, 0.0);
    }

    [TestMethod]
    public void Add()
    {
    PrivateObject po = new PrivateObject(new Medium());
    var value = po.Invoke("Add", 3,6);
    Assert.AreEqual(value, 9);
    }
    3
    为了应用程序的安全考虑,一些方法属性会被定义为private,那么private属性需要如何访问呢?
    GetParameterStrValue()方法主要用于调用私有属性及方法
    例子代码:
    public class Demo
    {
    Demo();

    private string _test="cs";
    private string test 
    {
    get { return _test; }
    set { _test = value; }
    }
    }

    [TestMethod]
    public void GetTest()
    {
    string Test="cs";
    Demo de =new Demo();
    var result = de.GetParameterStrValue("test");
    Assert.AreEqual(Test, result);
    }

    Fakes有两种形式:stub 和 shim。。

    一、shim
    以下将模拟DateTime的Now属性,假设我现在需要在活动服务类ActivityService添加一个方法验证某个线下活动是否过期。

    1. 打开VS2012,创建单元测试项目FakesTesting,我这是测试先行。重命名项目自动生成的类UnitTest1为ActivityServiceTest,将TestMethod1改为IsExpireTest(是否过期).

    2. 添加代码“ActivityService service = new ActivityService();”并使用VS快捷功能为我们创建ActivityService 类

    3. 添加Fakes,由于DateTime位于System程序集,因而将添加System的Fake程序集(右键System程序集), 然后在测试类“using System.Fakes;”

    4. 编写测试代码如下


    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.Fakes;
    using Microsoft.QualityTools.Testing.Fakes;

    namespace FakesTesting.Test
    {
    [TestClass]
    public class ActivityServiceTest
    {
    [TestMethod]
    public void IsExpireTest()
    {
    ActivityService service = new ActivityService();
    bool actual = service.IsExpire();
    Assert.IsFalse(actual);

    using (ShimsContext.Create())
    {
    ShimDateTime.NowGet = () => new DateTime(2014, 5, 5);
    actual = service.IsExpire();
    Assert.IsFalse(actual);
    }
    }
    }
    }

    5. 然后编写ActivityService类


    public class ActivityService
    {
    public DateTime BeginTime { get; set; }

    public ActivityService()
    {
    this.BeginTime = new DateTime(2014, 3, 3); //仅作演示,无意义
    }

    public bool IsExpire()
    {
    return BeginTime >= DateTime.Now;
    }
    }


    二、Stub
    现在假设ActivityService类有一个方法获取是否还能报名,但是它依赖于仓储IActivityRepository(只有遵循依赖反转与接口隔离原则的代码才好使用Stub填充外部依赖)提供的RegisterNumber方法。

    1. IActivityRepository接口(新建IRepositories项目并添加该接口)


    public interface IActivityRepository
    {
    /// <summary>
    /// 已报名人数
    /// </summary>
    int RegisterNumber();
    }

    2. 而我们的单元测试现在不能依赖具体(实际环境中的Repository可能对测试带来影响),这时候就能使用Stub来填充该接口了,添加IRepositories引用,然后与上一个Demo一样的添加IRepositories的Fakes程序集。

    3. 在测试类中添加Using代码

    using IRepositories;
    using IRepositories.Fakes;
    4. 编写测试代码


    [TestMethod]
    public void CanRegisterTest()
    {
    StubIActivityRepository repository = new StubIActivityRepository();
    ActivityService service = new ActivityService(repository);

    //如果已报名人数小于最多可报名数量则不能再报名,断言CanRegister方法应为True
    repository.RegisterNumber = ()=> 20;
    bool actual = service.CanRegister();
    Assert.IsTrue(actual);

    //如果已报名人数大于等于最多可报名数量则不能再报名,断言CanRegister方法应为False
    repository.RegisterNumber = () => 50;
    actual = service.CanRegister();
            Assert.IsFalse(actual);
          }


    5. ActivityService代码:


    public class ActivityService
    {
    public DateTime BeginTime { get; set; }

    /// <summary>
    /// 最多可报名数量
    /// </summary>
    private int maxCount = 50;
    private IActivityRepository repository;

    public ActivityService()
    {
    this.BeginTime = new DateTime(2014, 3, 3); //仅作演示,无意义
    }

    public ActivityService(IActivityRepository repository)
    {
    // TODO: Complete member initialization
    this.repository = repository;
    }

    public bool IsExpire()
    {
    return BeginTime >= DateTime.Now;
    }

    public bool CanRegister()
    {
    return repository.RegisterNumber() < this.maxCount;
    }
    }



    总结
    stub用于我们可控的代码,shim用于不可控的,例如.NET Framework以及第三方类库等。
  • 相关阅读:
    为什么RedLock并不能100%解决Redis做分布式锁的问题?
    阿里巴巴sentinel熔断降级(请求方是浏览器、请求方是应用两种方式考虑)
    阿里巴巴sentinel里面熔断里面RT是什么意思?(是什么单词的缩写)
    人生而孤独,却渴望陪伴。
    教你从官网下载commons-logging jar包(授人鱼不如授人以渔)
    spring5学习笔记-demo1
    企业网络规划
    scapy 查找自己的网卡(默认网卡)
    前端开发中常用的几种设计模式
    webpack打包原理
  • 原文地址:https://www.cnblogs.com/xinwuhen/p/5420151.html
Copyright © 2020-2023  润新知