zoukankan      html  css  js  c++  java
  • 在 .NET Core 项目中使用 xUnit.NET进行单元测试

    一、为什么要进行自动化测试

        

     自动化测试的分类

        

     单元测试:可以测试某个类或一个类的某个方法或功能,它具有非常好的深度。(大多是对行为进行测试public)

     集成测试:没有单元测试那么细致,可以测试功能系统的组合,以及数据库及文件系统的测试。

     皮下测试:具有很好的覆盖率,对于一个MVC结构的应用来说,它就是针对刚好在Controller下面一层的测试;对于Web Services来说,就是对于节点下面的测试。

     UI测试:深度欠佳。

     测试阶段

       

     xUnit.NET

          xUnit是一个测试框架,可以针对.net/core进行测试。

          测试项目需引用被项目从而对其进行测试,测试项目同时需要引用xUnit库。测试编写好后,用Test Runner来运行测试。Test Runner可以读取测试代码,并且会知道我们所使用的测试框架,然后执行,并显示结果。目前可用的Test Runner包括vs自带的Test Explorer,或者dotnet core命令行,以及第三方工具,例如resharper等等。

           

       特点:支持多平台/运行时,并行测试,数据驱动测试,可扩展。

       官网地址:https://xunit.net/

    简单的测试项目

       

         新建一个计算器的Demo之后,再添加xUnit测试项目DemoTest,结构如下:

       

      首先在Demo中写一个计算器,如下:

     public class Calculator
        {
            public int Add(int x,int y)
            {
                return x + y;
            }
        }

    然后去写测试代码:

    public class CalculatorTests
        {
            [Fact]
            public void ShouldAdd()
            {
                //Arrange
                var sut = new Calculator();//sut-System Under Test
    
                //Act
                var result = sut.Add(x:1,y:2);
    
                //Assert
                Assert.Equal(expected: 3, actual: result);
            }
        }

    我们用VS自带的测试器进行测试,如下:

    二、Assert

      Assert基于代码的返回值、对象的最终状态、事件是否发生等情况来评估测试的结果。

      Assert的jieg结果可能是Pass或者Fail,如果所有的asserts都pass了,那么整个测试就pass了;如果有任何assert fail了,那么测试就fail了。

      

     Assert类型:

      

    现在新建一个Patient类:

    public class Patient
        {
            public Patient()
            {
                IsNew = true;
            }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string FullName => $"{FirstName} {LastName}";
            public int HeartBeatRate { get; set; }
            public bool IsNew { get; set; }
            public void IncreaseHeartBeatRate()
            {
                HeartBeatRate = CalculateHeartBeatRate() + 2;
            }
            private int CalculateHeartBeatRate()
            {
                var random = new Random();
                return random.Next(1, 100);
            }
        }

      测试类PatientShould,首先写一个bool测试:

    public  class PatientShould
        {
            [Fact]
            public void HaveHeartBeatWhenNew()
            {
                //Arrange
                var patient = new Patient();
                //Act
                var result = patient.IsNew;
                //Assert
                Assert.True(result);
            }
        }

      string测试:

    [Fact]
            public void CalculateFullName()
            {
                var p = new Patient
                {
                    FirstName = "Nick",
                    LastName = "Carter"
                };
                var fullName = p.FullName;
                Assert.Equal(expected:"Nick Carter",actual: fullName);
                Assert.StartsWith(expectedStartString: "Nick", actualString: fullName);
                Assert.EndsWith(expectedEndString: "Carter", actualString: fullName);
                Assert.Contains(expectedSubstring: "Nick Carter", actualString: fullName);
            }

    数值型:

      [Fact]
            public void HaveDefaultBloodSugarWhenCreated()
            {
                var p = new Patient();
                var bloodSugar = p.BloodSuger;
    
                Assert.Equal(expected: 4.9f, actual: bloodSugar, precision: 5);
                Assert.InRange(actual:bloodSugar,low:3.9f,high:6.1f);
            }

    NULL:

     [Fact]
            public void HaveNoNameWhenCreate()
            {
                var p = new Patient();
                Assert.Null(p.FirstName);
                Assert.NotNull(p);
            }

    集合:

    [Fact]
            public void HaveHadAcoldBefore()
            {
                var p = new Patient();
                p.History.Add("感冒");
                p.History.Add("发烧");
                p.History.Add("肺炎");
    
                Assert.Contains(expected:"感冒",p.History);
                Assert.DoesNotContain(expected: "高血压", p.History);
                Assert.Contains(p.History,filter:x=>x.StartsWith(value:""));
            }

    Object:

    新建一个Person类,Patient继承Person:

    [Fact]
            public void BeAPerson()
            {
                var p = new Patient();
                var p1 = new Patient();
                Assert.IsType<Patient>(p);
                Assert.IsNotType<Person>(p);
                Assert.IsAssignableFrom<Person>(p);
                //Assert.Same(p, p1);
            }

    测试:

    异常:

    在Patient类里初始化 throw new InvalidOperationException(message:"Not able Create");

    [Fact]
            public void ThrowExceptionWhenErrowOccurred()
            {
                Assert.Throws<InvalidOperationException>(testCode: () => new Patient());
            }

    测试:

     

     三、分组,忽略,Log,上下文

    1、测试分组

      [Trait("Name","Vaue")]   方法级,Class级

    [Fact]
            [Trait("Category","New")]
            public void HaveHeartBeatWhenNew()
            {
                //Arrange
                var patient = new Patient();
                //Act
                var result = patient.IsNew;
                //Assert
                Assert.True(result);
            }
    [Trait("Category", "Calc")]
        public class CalculatorTests
        {
            [Fact]
            public void ShouldAdd()
            {
                //Arrange
                var sut = new Calculator();//sut-System Under Test
    
                //Act
                var result = sut.Add(x:1,y:2);
    
                //Assert
                Assert.Equal(expected: 3, actual: result);
            }
        }

    2、忽略测试

    [Fact(Skip = "不跑这个测试")]

     3、自定义测试输出信息

       ITestOutputHelper

    private readonly ITestOutputHelper _outputHelper;
            public PatientShould(ITestOutputHelper testOutput)
            {
                _outputHelper = testOutput;
            }
    
            [Fact]
            [Trait("Category","New")]
            public void HaveHeartBeatWhenNew()
            {
                _outputHelper.WriteLine("第一个测试");
                //Arrange
                var patient = new Patient();
                //Act
                var result = patient.IsNew;
                //Assert
                Assert.True(result);
            }

    4、共享上下文

     public class LongTimeTask
        {
            public LongTimeTask()
            {
                Thread.Sleep(millisecondsTimeout:2000);
            }
        }
    private readonly ITestOutputHelper _outputHelper;
            private readonly LongTimeTask longTimeTask;
            public PatientShould(ITestOutputHelper testOutput)
            {
                _outputHelper = testOutput;
                longTimeTask = new LongTimeTask();
            }

    任意跑两个测试方法:

     每个测试方法时间都是2秒了,为了解决这个问题

    public class LongTimeTaskFixture:IDisposable
        {
            public LongTimeTask Task { get; set; }
    
            public LongTimeTaskFixture()
            {
                Task = new LongTimeTask();
            }
    
            public void Dispose()
            {
               
            }
        }
     private readonly ITestOutputHelper _outputHelper;
            private readonly LongTimeTask longTimeTask;
            public PatientShould(ITestOutputHelper testOutput,LongTimeTaskFixture timeTaskFixture)
            {
                _outputHelper = testOutput;
                longTimeTask = timeTaskFixture.Task;
            }

     多个类共用一个

    [CollectionDefinition("Long Time Task Collection")]
       public class TaskCollection:ICollectionFixture<LongTimeTaskFixture>
        {
        }
     [Collection("Long Time Task Collection")]

    四、数据驱动测试

     [Theory]

    [Fact]
            public void ShouldAdd()
            {
                //Arrange
                var sut = new Calculator();//sut-System Under Test
    
                //Act
                var result = sut.Add(x:1,y:2);
    
                //Assert
                Assert.Equal(expected: 3, actual: result);
            }
    
            [Fact]
            public void ShouldAdd2()
            {
                //Arrange
                var sut = new Calculator();//sut-System Under Test
    
                //Act
                var result = sut.Add(x: 1, y: 3);
    
                //Assert
                Assert.Equal(expected: 4, actual: result);
            }

    这种写法比较傻,重复代码多,下面是新写法:

    1、 [InlineData(x,y,.....)]

           [Theory]
            [InlineData(1, 2, 3)]
            [InlineData(2, 2, 4)]
            [InlineData(3, 2, 5)]
            public void ShouldAdd(int x,int y,int expected)
            {
                //Arrange
                var sut = new Calculator();//sut-System Under Test
    
                //Act
                var result = sut.Add(x,y);
    
                //Assert
                Assert.Equal(expected, actual: result);
            }

    2、[MemberData(nameof(xxx),MemberType=typeof(xxx))]

     public class CalculatorTestsData
        {
            private static readonly List<object[]> Data = new List<object[]>
            {
                new object[]{1,2,3},
                new object[]{2,3,5},
                new object[]{2,4,6}
            };
            public static IEnumerable<object[]> TestData => Data;
        }
    [Theory]
            [MemberData(nameof(CalculatorTestsData.TestData),MemberType =typeof(CalculatorTestsData))]
            public void ShouldAdd(int x,int y,int expected)
            {
                //Arrange
                var sut = new Calculator();//sut-System Under Test
    
                //Act
                var result = sut.Add(x,y);
    
                //Assert
                Assert.Equal(expected, actual: result);
            }

    执行结果:

     3、外部数据源

     新建一个csv文件,如下:

    public  class CalculatorCvsData
        {
            public static IEnumerable<object[]> TestData
            {
                get
                {
                    string[] csvlines = File.ReadAllLines(path:"Data\TestData.csv");
                    var testCases = new List<object[]>();
                    foreach(var csvline in csvlines)
                    {
                        IEnumerable<int> values = csvline.Split(separator: ',').Select(int.Parse);
                        object[] testCase = values.Cast<object>().ToArray();
                        testCases.Add(testCase);
                    }
                    return testCases;
                }
            }
        }
    [Theory]
            [MemberData(nameof(CalculatorCvsData.TestData),MemberType =typeof(CalculatorCvsData))]
            public void ShouldAdd(int x,int y,int expected)
            {
                //Arrange
                var sut = new Calculator();//sut-System Under Test
    
                //Act
                var result = sut.Add(x,y);
    
                //Assert
                Assert.Equal(expected, actual: result);
            }

    4、自定义Data Attribute

     public class CalculatorDataAttribute: DataAttribute
        {
            public override IEnumerable<object[]> GetData(MethodInfo methodInfo)
            {
                yield return new object[] { 0, 100, 100 };
                yield return new object[] { 1, 100, 101 };
                yield return new object[] { 200, 100, 300 };
                yield return new object[] { 15, 100, 115};
            }
        }
    [CalculatorData]
            public void ShouldAdd(int x,int y,int expected)
            {
                //Arrange
                var sut = new Calculator();//sut-System Under Test
    
                //Act
                var result = sut.Add(x,y);
    
                //Assert
                Assert.Equal(expected, actual: result);
            }

    运行测试:

     至此,xunit.Net学习到此结束了。

  • 相关阅读:
    用C++做微信公众平台开发的后台开发时,用sha1加密验证的方法
    UART Receive FIFO and Receive Timeout
    Compile cpp File Manually without IDE under Mingw Environment
    html5 返回当前地理位置的坐标点(经纬度)
    逆袭!花两个月吃透这份“MySQL宝典”拿到字节offer
    MySQL约束的概述
    2020-11-28
    人工智能能力提升指导总结
    年轻就该多尝试,教你20小时Get一项新技能
    MySQL~存储过程基本操作
  • 原文地址:https://www.cnblogs.com/zhangyunyun/p/12535297.html
Copyright © 2011-2022 走看看