zoukankan      html  css  js  c++  java
  • 单元测试实践

    前言

    在此,只是对下面这条链接的实现:

    https://www.cnblogs.com/AlexanderZhao/p/12369732.html

    正文

    测试分类:

    1.单元测试

    2.集成测试

    3.皮下测试

    4.UI测试

    测试的三个阶段:AAA

    Arrange: 在这里做一些先决的设定。例如创建对象实例,数据,输入等。
    
    Act: 在这里执行生产代码并返回结果。例如调用方法或者设置属性。
    
    Assert:在这里检查结果,会产生测试通过或者失败两种结果。
    

    Assert方法应用

    bool 值:

    Patient patient = new Patient();
    var result = patient.IsNew;
    Assert.True(result);
    Assert.False(result);
    

    string 类型:

    [Fact]
    public async void Test1()
    {
    	Patient patient = new Patient();
    	Assert.Equal("xxx",patient.FistName);
    	Assert.NotEqual("xxx",patient.LastName);
    	Assert.StartsWith("xxx",patient.LastName);
    	Assert.EndsWith("xxx",patient.LastName);
    	Assert.Contains("xxx",patient.LastName);
    	Assert.Matches(@"xxx",patient.LastName);
    }
    

    数字类型:

    Patient patient = new Patient();
    var result = patient.Age;
    Assert.Equal(4.9f,result);
    Assert.InRange(result,3.9,6.1);
    

    判断null 和 not null

    var p = new Patient();
    Assert.Null(p.FirstName);
    Assert.NotNull(_patient);
    

    集合:

    Patient patient = new Patient();
    var diseases = new List<string>
    {
    	"感冒",
    	"发烧",
    	"水痘",
    	"腹泻"
    };
    Assert.Contains("感冒",patient.History);
    Assert.DoesNotContain("感冒", patient.History);
    // any
    Assert.Contains(patient.History,x=>x.StartsWith("水"));
    // 遍历 all
    Assert.All(patient.History, x => { Assert.True(x.Length > 2); });
    Assert.Equal(diseases,patient.History);
    

    对象:

    Patient patient = new Patient();
    Patient patient1 = new Patient();
    // 类型是否相同
    Assert.IsNotType<Patient>(patient);
    Assert.IsType<Patient>(patient);
    // 两个实例是否是同一个
    Assert.Same(patient,patient1);
    Assert.NotSame(patient, patient1);
    //是否继承Patient
    Assert.IsAssignableFrom<Patient>(patient);
    

    错误性验证:

    Patient patient = new Patient();
    // 判断是否出现指定的异常
    var ex = Assert.Throws<InvalidOperationException>(()=> { patient.NotAllowed(); });
    // 进一步判断异常消息
    Assert.Equal("not able to create", ex.Message);
    

    判断是否触发事件:

    /// <summary>
    /// 判断是否触发事件
    /// </summary>
    [Fact]
    public void RaizeSleepEvent()
    {
        var p = new Patient();
        Assert.Raises<EventArgs>(
            handler=>p.PatientSlept+=handler,
            handler=>p.PatientSlept -= handler,
            () => p.Sleep());
    }
    

    判断属性改变是否触发事件#

    /// <summary>
    /// 测试属性改变事件是否触发
    /// </summary>
    [Fact]
    public void RaisePropertyChangedEvent()
    {
        var p = new Patient();
        Assert.PropertyChanged(p, nameof(p.HeartBeatRate),
                               () => p.IncreaseHeartBeatRate());
    }
    

    进阶

    分组测试:

    [Fact]
    [Trait("Category", "New")]
    public void BeNewWhenCreated()
    {
    	var patient = new Patient();
    	patient.IsNew = true;
    	var result = patient.IsNew;
    	Assert.True(result);
    }
    
    [Fact]
    [Trait("Category", "add")]
    public void BeNewWhenCreated1()
    {
    	var patient = new Patient();
    	var result = patient.IsNew;
    	Assert.True(result);;
    }
    
    dotnet test --filter “Category=New” //运行单个分类测试
    dotnet test --filter “Category=New|Category=Add”//运行多个分类测试
    dotnet test --filter Category --logger:trx //输出测试日志
    

    忽略测试#

    使用特性:[Fact(Skip="不跑这个测试")],可以忽略测试,忽略测试图标为黄色警告

    自定义测试输出内容#

    使用ITestOutputHelper可以自定义在测试时的输出内容
    dotnet test --filter Category --logger:trx会输出测试日志trx结尾的文件

    public class PatientShould:IClassFixture<LongTimeFixture>,IDisposable
    {
        private readonly ITestOutputHelper _output;
        private readonly Patient _patient;
        private readonly LongTimeTask _task;
        public PatientShould(ITestOutputHelper output,LongTimeFixture fixture)
        {
            this._output = output;
            _patient = new Patient();
            //_task = new LongTimeTask();
            _task = fixture.Task;
        }
    
        [Fact]
        [Trait("Category","New")]
        public void BeNewWhenCreated()
        {
            _output.WriteLine("第一个测试");
            // Arrange
            //var patient = new Patient();
            // Act
            var result = _patient.IsNew;
            // Assert
            Assert.True(result);
            //Assert.False(result);
        }
    }
    

    减少重复代码#

    减少new对象,可以在构造函数中new,在方法中使用。
    测试类实现IDispose接口,测试完释放资源,注意每个测试结束后都会调用Dispose方法。
    

    共享上下文#
    同一个测试类#

    在执行一个方法时,需要很长事件,而在构造函数中new时,每个测试跑的时候都会new对象或者执行方法,这是导致测试很慢。解决方法:

    创建一个类:

    using Demo2;
    using System;
    
    namespace Demo2Test
    {
        public class LongTimeFixture : IDisposable
        {
            public LongTimeTask Task { get; }
            public LongTimeFixture()
            {
    
            }
            public void Dispose()
            {
            }
        }
    }
    

    测试类实现IClassFixture接口,并在构造函数中获取方法

    public class PatientShould:IClassFixture<LongTimeFixture>,IDisposable
    {
        private readonly ITestOutputHelper _output;
        private readonly Patient _patient;
        private readonly LongTimeTask _task;
        public PatientShould(ITestOutputHelper output,LongTimeFixture fixture)
        {
            this._output = output;
            _patient = new Patient();
            //_task = new LongTimeTask();
            _task = fixture.Task;//获取方法
        }
    }
    

    不同的测试类#

    1.在上一个的继承上,先建立一个TaskCollection类,实现ICollectionFixture接口,注意不能有副作用,否则会影响结果

    using Xunit;
    
    namespace Demo2Test
    {
        [CollectionDefinition("Lone Time Task Collection")]
        public class TaskCollection:ICollectionFixture<LongTimeFixture>
        {
        }
    }
    

    使用,加上[Collection("Lone Time Task Collection")]

    [Collection("Lone Time Task Collection")]
    public class PatientShould:IClassFixture<LongTimeFixture>,IDisposable
    {
        private readonly ITestOutputHelper _output;
        private readonly Patient _patient;
        private readonly LongTimeTask _task;
        public PatientShould(ITestOutputHelper output,LongTimeFixture fixture)
        {
            this._output = output;
            _patient = new Patient();
            //_task = new LongTimeTask();
            _task = fixture.Task;//获取方法
        }
    }
    

    数据共享

    1. 使用[Theory],可以写有构造参数的测试方法,使用InlineData传递数据#
    [Theory]
    [InlineData(1,2,3)]
    [InlineData(2,2,4)]
    [InlineData(3,3,6)]
    public void ShouldAddEquals(int operand1,int operand2,int expected)
    {
        //Arrange
        var sut = new Calculator(); //sut-system under test
        //Act
        var result = sut.Add(operand1, operand2);
        //Assert
        Assert.Equal(expected, result);
    }
    

    2. 使用[MemberData]特性,可以在多个测试中使用#

    先添加CalculatorTestData类:

    using System.Collections.Generic;
    
    namespace DemoTest
    {
        public  class CalculatorTestData
        {
            private static readonly List<object[]> Data = new List<object[]>
            {
                new object[]{ 1,2,3},
                new object[]{ 1,3,4},
                new object[]{ 2,4,6},
                new object[]{ 0,1,1},
            };
    
            public static IEnumerable<object[]> TestData => Data;
        }
    }
    

    使用MemberData

    /// <summary>
    /// 数据共享-MemberData
    /// </summary>
    /// <param name="operand1"></param>
    /// <param name="operand2"></param>
    /// <param name="expected"></param>
    [Theory]
    [MemberData(nameof(CalculatorTestData.TestData),MemberType =typeof(CalculatorTestData))]
    public void ShouldAddEquals2(int operand1, int operand2, int expected)
    {
        //Arrange
        var sut = new Calculator(); //sut-system under test
        //Act
        var result = sut.Add(operand1, operand2);
        //Assert
        Assert.Equal(expected, result);
    }
    

    3. 使用外部数据

    先创建一个类,准备数据,这里是读取的csv文件的数据
    
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    
    namespace DemoTest.Data
    {
        /// <summary>
        /// 读取文件并返回数据集合
        /// </summary>
        public class CalculatorCsvData
        {
            public static IEnumerable<object[]> TestData
            {
                get
                {
    	            //把csv文件中的数据读出来,转换
                    string[] csvLines = File.ReadAllLines("Data\TestData.csv");
                    var testCases = new List<object[]>();
                    foreach (var csvLine in csvLines)
                    {
                        IEnumerable<int> values = csvLine.Trim().Split(',').Select(int.Parse);
                        object[] testCase = values.Cast<object>().ToArray();
                        testCases.Add(testCase);
                    }
                    return testCases;
                }
            }
        }
    }
    

    2.csv数据

    Copy
    1,2,3
    1,3,4
    2,4,6
    0,1,1

    /// <summary>
    /// 数据共享-MemberData-外部数据
    /// </summary>
    /// <param name="operand1"></param>
    /// <param name="operand2"></param>
    /// <param name="expected"></param>
    [Theory]
    [MemberData(nameof(CalculatorCsvData.TestData), MemberType = typeof(CalculatorCsvData))]
    public void ShouldAddEquals3(int operand1, int operand2, int expected)
    {
        //Arrange
        var sut = new Calculator(); //sut-system under test
        //Act
        var result = sut.Add(operand1, operand2);
        //Assert
        Assert.Equal(expected, result);
    }
    
    1. 使用自定义特性,继承自DataAttribute#

      自定义特性

    using System.Collections.Generic;
    using System.Reflection;
    using Xunit.Sdk;
    
    namespace DemoTest.Data
    {
        public class CalculatorDataAttribute : DataAttribute
        {
            public override IEnumerable<object[]> GetData(MethodInfo testMethod)
            {
                yield return new object[] { 0, 100, 100 };
                yield return new object[] { 1, 99, 100 };
                yield return new object[] { 2, 98, 100 };
                yield return new object[] { 3, 97, 100 };
            }
        }
    }
    
    /// <summary>
    /// 数据共享-自定义特性继承自DataAttribute
    /// </summary>
    /// <param name="operand1"></param>
    /// <param name="operand2"></param>
    /// <param name="expected"></param>
    [Theory]
    [CalculatorDataAttribute]
    public void ShouldAddEquals4(int operand1, int operand2, int expected)
    {
        //Arrange
        var sut = new Calculator(); //sut-system under test
        //Act
        var result = sut.Add(operand1, operand2);
        //Assert
        Assert.Equal(expected, result);
    }
    
  • 相关阅读:
    Sum Root to Leaf Numbers 解答
    459. Repeated Substring Pattern
    71. Simplify Path
    89. Gray Code
    73. Set Matrix Zeroes
    297. Serialize and Deserialize Binary Tree
    449. Serialize and Deserialize BST
    451. Sort Characters By Frequency
    165. Compare Version Numbers
    447. Number of Boomerangs
  • 原文地址:https://www.cnblogs.com/aoximin/p/12704317.html
Copyright © 2011-2022 走看看