在 .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学习到此结束了。

 

posted @ 2020-03-21 12:07  圆圆酥  阅读(605)  评论(0编辑  收藏  举报