当前位置: 首页 > 工具软件 > xUnit.net > 使用案例 >

xUnit.NET对.NET CORE测试

太叔烨霖
2023-12-01

xUnit.NET对.NET CORE测试

软件工艺师原作者

含义

XUNIT是一个测试框架,可以针对.net/core测试。一般是针对类的Public方法进行测试,也就是对行为进行测试,如果是私有方法需要改变修饰符才能测试。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2tuTM5sP-1679620493695)(C:\Users\29310\AppData\Roaming\Typora\typora-user-images\image-20230322135913491.png)]

特点

  • 支持多平台/运行时
  • 平进行测试
  • 数据驱动测试
  • 可扩展

自动化测试分类

  • 单元测试可以测试某个类或方法,具有较高的深度,对应用的功能覆盖面很小。
  • 集成测试有更好的广度,可以测试web资源,数据库资源等。
  • 皮下测试在web中针对controller下的节点测试。
  • UI测试是对应用的界面功能测试。
  • 实际上常用的是单元测试和集成测试。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CmwyLtzH-1679620493696)(C:\Users\29310\AppData\Roaming\Typora\typora-user-images\image-20230322140456987.png)]

支持平台

  • .Net Framework

  • .Net Core

  • .Net Standard:.NET Standard是一套协议规范,不像.NET Framework,后者是一个完整的技术框架,而前者只是定义了一套规范,遵守这套规范的.NET Standard类库可以被不同.NET框架引用,比如.NET Core项目和.NET Framework项目都可以引用这个类库。

    .NET Standard详解_begeneral的博客-CSDN博客

  • UWP:在Windows 10 Mobile/Surface(Windows平板电脑)/PC/Xbox/HoloLens等平台上运行,uwp不同于传统pc上的exe应用,也跟只适用于手机端的app有本质区别。它并不是为某一个终端而设计,而是可以在所有windows10设备上运行

  • Xamarin: 是一个开放源代码平台,用于通过 .NET 构建适用于 iOS、Android 和 Windows 的新式高性能应用程序。 Xamarin 是一个抽象层,可管理共享代码与基础平台代码的通信。 Xamarin 在提供便利(如内存分配和垃圾回收)的托管环境中运行。

xUnit官网

Home > xUnit.net

测试三个阶段

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fdyhJtaR-1679620493697)(C:\Users\29310\AppData\Roaming\Typora\typora-user-images\image-20230322142648593.png)]

代码

 [Fact]//表示当前是测试方法 标记的测试方法不能有方法参数
        public void ShouldAdd()
        {
            // Arrange
            var sut = new Demo.calculate();//sut - System Under Test

            // Act
            var result = sut.Add(2,3);

            // Assert
            Assert.Equal(5,result);//前面第一个参数是我们想要的结果,第二个是测试的结果
        }

运行

第一种是直接在里面用vs运行。

第二种cmd:找到当前的xunit文件位置 dotnet test执行所有测试,dotnet test --help帮助

Assert

含义

Assert基于代码的返回值、对象的最终状态、事件是否发生等情况来评估测试的结果
Assert的结果可能是Pass或者Fail
如果所有的asserts都通过了,那么整个测试就通过了。
如果任何assert 失败了,那么结果就失败了。

类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kDdXZAv0-1679620493697)(C:\Users\29310\AppData\Roaming\Typora\typora-user-images\image-20230322144717063.png)]

问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTWRYTBi-1679620493697)(C:\Users\29310\AppData\Roaming\Typora\typora-user-images\image-20230322144824151.png)]

代码

using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace DemoTest
{
    public class PatientShould
    {
        /// <summary>
        /// bool类型
        /// </summary>
        [Fact]
        public void Benewisture()
        {
            // Arrange
            var sut = new Demo.Patient();

            // Act
            var result = sut.IsNew;

            // Assert
            Assert.True(result);
        }
        /// <summary>
        /// string类型
        /// </summary>
        [Fact]
        public void FullNameTest()
        {
            var sut = new Demo.Patient()
            {
                FirstName = "懒",
                LastName="洋洋"
            };
            var result = sut.FullName;
            Assert.Equal("懒洋洋",result);
            Assert.StartsWith("懒",result);
            Assert.EndsWith("洋洋",result);
            Assert.Contains("洋",result);
        }
        /// <summary>
        /// 数据类型
        /// </summary>
        [Fact]
        public void bloodSugarValueTest()
        {
            var sut = new Demo.Patient();
            var result = sut.bloodSugar;
            Assert.Equal(4.9f,result);
            Assert.InRange(result,3.6f,6.1f);//区间值
        }
        /// <summary>
        /// class类型
        /// </summary>
        [Fact]
        public void PatientIsNull()
        {
            var sut = new Demo.Patient();
            Assert.Null(sut.FirstName);
            //Assert.NotNull(sut.FirstName);
        }
        /// <summary>
        /// list类型
        /// </summary>
        [Fact]
        public void HistroyAddOrSelect()
        {
            var sut = new Demo.Patient();
            sut.Histroy.Add("社牛");
            sut.Histroy.Add("社恐");
            sut.Histroy.Add("挖野菜");
            sut.Histroy.Add("恋爱脑");

            List<string> Health = new List<string>()
            {
                "社牛",
                "社恐",
                "挖野菜",
                "恋爱脑"
            };

            Assert.Contains("挖野菜", sut.Histroy);
            Assert.DoesNotContain("IKUN",sut.Histroy);
            //Assert.Contains(sut.Histroy,u=>u.StartsWith("牛"));
            Assert.Contains(sut.Histroy,u=>u.Length>=2);
            Assert.Equal(Health,sut.Histroy);
        }
        /// <summary>
        /// 判断是否是一个实列
        /// </summary>
        [Fact]
        public void BeAPerson()
        {
            var p = new Demo.Patient();
            var pr = new Demo.Person();
            Assert.IsType<Demo.Patient>(p);
            Assert.IsNotType<Demo.Person>(p);
            Assert.IsAssignableFrom<Demo.Person>(p);
            Assert.NotSame(p,pr);
            //Assert.Same(p, pr);//是不是一个实列

        }
        /// <summary>
        /// 异常
        /// </summary>
        [Fact]
        public void ExpretionError()
        {
            var ex = Assert.Throws<InvalidOperationException>(()=>new Demo.Patient().NotMessage());
            Assert.Equal("不能执行这个东西",ex.Message);
        }
        /// <summary>
        /// 事件是否执行
        /// </summary>
        [Fact]
        public void functionExecution()
        {
            var p = new Demo.Patient();
            Assert.Raises<EventArgs>(
                handler => p.PatienSlept += handler,
                handler => p.PatienSlept -= handler,
                () => p.Sleep());
        }
        /// <summary>
        /// 事件是否改变
        /// </summary>
        [Fact]
        public void functionUpdate()
        {
            var p = new Demo.Patient();
            Assert.PropertyChanged(p, nameof(p.HeartBeatRate), () => p.IncreaseHeartBeatRate());
        }
    }
}

using Hangfire.Annotations;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;

namespace Demo
{
    public class Patient: Person, INotifyPropertyChanged
    {
        public Patient() 
        {
            IsNew = true;
            bloodSugar = 4.9f;//这里记录下这个加f是什么意思是为了防止丢失精度 
            Histroy = new List<string>();
        }
        public string FullName => $"{FirstName}{LastName}";
        public int HeartBeatRate { get; set; }
        public bool IsNew { get; set; }
        public float bloodSugar { get; set; }
        public List<string> Histroy { get; set; }
        public event EventHandler<EventArgs> PatienSlept;//event 事件 事件委托类型
        public event PropertyChangedEventHandler PropertCyhanged;
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void Onchangenoti([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public void OnPatienSlept()
        {
            PatienSlept?.Invoke(this,EventArgs.Empty);//为委托事件没有数据的提供数据
        }
        public void Sleep()
        {
            OnPatienSlept();
        }
        public void IncreaseHeartBeatRate()
        {
            HeartBeatRate = CalculateHeartBeatRate() + 2;
            Onchangenoti(nameof(HeartBeatRate));
        }
        private int CalculateHeartBeatRate()
        {
            var random = new Random();
            return random.Next(1,100);
        }
        public void NotMessage()
        {
            throw new InvalidOperationException("不能执行这个东西");
        }
         
    }

 
}

测试分组

[Trait(‘name’,‘value’)]

在分组中选择特征,可以将他作用到类上面和方法上面

cmd指令:dotnet test --filter Category=New 单个 dotnet test --filter “Category=New|Category=Name”

忽略

[Fact(Skip =“不需要跑这个测试”)]

自定义输出信息

cmd指令:dotnet test --filter Category=New --logger:trx

重构代码

写提示

  private readonly ITestOutputHelper _outHelp;
  _task =fixture.Task;

IDisposable实现接口

		public void Dispose()
        {
            _outHelp.WriteLine("清理资源");
        }

共享类

[Collection("Long Time Task Collection")]

数据共享

[InlineData()]

 //第一种数据来源
        [Theory]
        [InlineData(1,2,3)]
        [InlineData(4,2,6)]
        [InlineData(5,2,7)]
        [InlineData(6,2,8)]
        public void ShouldAdd(int x,int y,int expression)
        {
            var sut = new Demo.calculate();
            var result = sut.Add(x, y);
            Assert.Equal(expression, result);
        }

[MemberData(nameof(calculateDate.TestDate),MemberType =typeof(calculateDate))]

 //第二种数据来源
        [Theory]
        [MemberData(nameof(calculateDate.TestDate),MemberType =typeof(calculateDate))]
        public void ShouldAdd2(int x, int y, int expression)
        {
            var sut = new Demo.calculate();
            var result = sut.Add(x, y);
            Assert.Equal(expression, result);
        }

外部数据来源

先声明一个csv文件写入数据(将属性改为复制)

然后写一个类接受数据

 /// <summary>
    /// 读取文件并返回数据集合
    /// </summary>
    public class CalculatorCsvData
    {
        public static IEnumerable<object[]> TestData
        {
            get
            {
                //把csv文件中的数据读出来,转换
                string[] csvLines = File.ReadAllLines("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;
            }
        }
    }

自定义特性继承自DataAttribute

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Xunit.Sdk;

namespace DemoTest
{
    public class CalculatorDataAttribute : DataAttribute
    {
        public override IEnumerable<object[]> GetData(MethodInfo testMethod)
        {
            yield return new object[] { 0, 100, 100 };//yield 提供下一个元素
            yield return new object[] { 1, 99, 100 };
            yield return new object[] { 2, 98, 100 };
            yield return new object[] { 3, 97, 100 };
        }
    }
}


est
{
public class CalculatorDataAttribute : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[] { 0, 100, 100 };//yield 提供下一个元素
yield return new object[] { 1, 99, 100 };
yield return new object[] { 2, 98, 100 };
yield return new object[] { 3, 97, 100 };
}
}
}


 类似资料: