From 4d4a664342ba1d3badaecbda30c7de78b98b9d1e Mon Sep 17 00:00:00 2001 From: bicijinlian Date: Thu, 29 Nov 2018 16:01:18 +0800 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8ce3e1acdf718a2259bb798a0428d2dd22a36d0e Author: bicijinlian Date: Sat Aug 4 22:58:33 2018 +0800 Controller 测试 commit 495b9dbf9e6b3b0503ddedf97ca085a7bd47e735 Author: bicijinlian Date: Sat Aug 4 22:47:02 2018 +0800 完善断言 commit 81b141418ab155e643d94284f36b8d13e6480dff Author: bicijinlian Date: Sat Aug 4 11:47:29 2018 +0800 共享数据完成 commit a6a5566d3a58886cf859637a4ccd153eb6faa8fd Author: bicijinlian Date: Sat Aug 4 10:01:12 2018 +0800 共享数据 commit 0ca4b3541d86df607b30dc86eb34799ffe512c31 Author: bicijinlian Date: Fri Aug 3 22:37:17 2018 +0800 测试类共享数据 commit 5aab2368cae6f59bfc21922d518ed0d84d96068b Author: bicijinlian Date: Fri Aug 3 17:40:49 2018 +0800 Assert 基本学完 commit c8e735b831a8fb73d6f53aedf6698d448f481c42 Author: bicijinlian Date: Fri Aug 3 17:13:43 2018 +0800 study commit 257168e8ce474a4808d5975c3990c6b7cdbc4327 Author: bicijinlian Date: Fri Aug 3 16:32:44 2018 +0800 Contains 学习 commit 34c3763943aa62885d8c5064aed2c40e1508400a Author: bicijinlian Date: Fri Aug 3 00:24:58 2018 +0800 断言学习 commit 763b9c33f122d70ef6b306bad2d054b4e36e0a7f Author: bicijinlian Date: Thu Aug 2 20:22:24 2018 +0800 断言学习 commit b1f9945fab2cb9e9fa95aff1e4947ed029442a48 Author: bicijinlian Date: Wed Aug 1 22:45:14 2018 +0800 study commit b7c75492279c578c2ac2659b34eb5bcc4756a0de Author: bicijinlian Date: Wed Aug 1 22:22:50 2018 +0800 分组优化 commit d72a992d72e68787d20a3535cfc348494eac5ad3 Author: bicijinlian Date: Wed Aug 1 22:12:34 2018 +0800 xUnit 学习 commit 3075a8b33ff47222ac262d05fe22c6688cc8c648 Author: bicijinlian Date: Wed Aug 1 08:51:25 2018 +0800 开发常规提交 commit 98e4059cb7545552e0cb506a2425f72de08ac176 Author: bicijinlian Date: Tue Jul 31 07:34:03 2018 +0800 Study commit 2615d3dedfcde8659748d1fe76e35c5bbf4a5068 Author: bicijinlian Date: Mon Jul 30 23:04:31 2018 +0800 Autofac commit 8fcd062f42e9151d4551361eed1c4d1544098c64 Merge: cd3b362 8639734 Author: bicijinlian Date: Mon Jul 30 15:52:53 2018 +0800 Merge tag 'ReleaseV1.0' into develop V1.0 --- xUnitStudy.Model/xUnitStudy.Model.csproj | 6 + .../xUnitStudy/AssertEqualDemo.cs | 25 + .../xUnitStudy/ClassFixtureDemo.cs | 63 + .../xUnitStudy/CollectionFixtureDemo.cs | 38 + xUnitStudy.Model/xUnitStudy/IPerson.cs | 19 + xUnitStudy.Model/xUnitStudy/Person.cs | 53 + xUnitStudy.Model/xUnitStudy/Teacher.cs | 13 + xUnitStudy.WebApi.Test/ApiControllerTest.cs | 29 + .../AutoFac/AutofacStudy.cs | 20 + .../Properties/AssemblyInfo.cs | 4 + .../StudentBll/StudentBllTest.cs | 101 +- xUnitStudy.WebApi.Test/TestCaseOrderTest.cs | 34 + xUnitStudy.WebApi.Test/UseAssertTest.cs | 1646 +++++++++++++++++ .../UseFixture/CollectionFixtureSetup.cs | 27 + .../UseFixture/UseClassFixtureTest.cs | 121 ++ .../UseFixture/UseCollectionFixtureTest.cs | 87 + .../UseFixture/UseFixtureTest.cs | 67 + .../UseFixture/UseFullFixtures.cs | 45 + xUnitStudy.WebApi.Test/UseOutputTest.cs | 27 + xUnitStudy.WebApi.Test/UseXUnitTest.cs | 104 +- xUnitStudy.WebApi.Test/app.config | 7 + xUnitStudy.WebApi.Test/packages.config | 4 + .../xUnitStudy.WebApi.Test.csproj | 27 +- 23 files changed, 2556 insertions(+), 11 deletions(-) create mode 100644 xUnitStudy.Model/xUnitStudy/AssertEqualDemo.cs create mode 100644 xUnitStudy.Model/xUnitStudy/ClassFixtureDemo.cs create mode 100644 xUnitStudy.Model/xUnitStudy/CollectionFixtureDemo.cs create mode 100644 xUnitStudy.Model/xUnitStudy/IPerson.cs create mode 100644 xUnitStudy.Model/xUnitStudy/Person.cs create mode 100644 xUnitStudy.Model/xUnitStudy/Teacher.cs create mode 100644 xUnitStudy.WebApi.Test/ApiControllerTest.cs create mode 100644 xUnitStudy.WebApi.Test/AutoFac/AutofacStudy.cs create mode 100644 xUnitStudy.WebApi.Test/TestCaseOrderTest.cs create mode 100644 xUnitStudy.WebApi.Test/UseAssertTest.cs create mode 100644 xUnitStudy.WebApi.Test/UseFixture/CollectionFixtureSetup.cs create mode 100644 xUnitStudy.WebApi.Test/UseFixture/UseClassFixtureTest.cs create mode 100644 xUnitStudy.WebApi.Test/UseFixture/UseCollectionFixtureTest.cs create mode 100644 xUnitStudy.WebApi.Test/UseFixture/UseFixtureTest.cs create mode 100644 xUnitStudy.WebApi.Test/UseFixture/UseFullFixtures.cs create mode 100644 xUnitStudy.WebApi.Test/UseOutputTest.cs diff --git a/xUnitStudy.Model/xUnitStudy.Model.csproj b/xUnitStudy.Model/xUnitStudy.Model.csproj index 6fdccdd..0ec04d7 100644 --- a/xUnitStudy.Model/xUnitStudy.Model.csproj +++ b/xUnitStudy.Model/xUnitStudy.Model.csproj @@ -45,6 +45,12 @@ + + + + + + diff --git a/xUnitStudy.Model/xUnitStudy/AssertEqualDemo.cs b/xUnitStudy.Model/xUnitStudy/AssertEqualDemo.cs new file mode 100644 index 0000000..bce71cf --- /dev/null +++ b/xUnitStudy.Model/xUnitStudy/AssertEqualDemo.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace xUnitStudy.Model +{ + public class AssertEqualDemo : IEqualityComparer + { + public int Id { get; set; } + + public string Name { get; set; } + + public bool Equals(AssertEqualDemo x, AssertEqualDemo y) + { + return x.Id == y.Id; + } + + public int GetHashCode(AssertEqualDemo obj) + { + return Id; + } + } +} diff --git a/xUnitStudy.Model/xUnitStudy/ClassFixtureDemo.cs b/xUnitStudy.Model/xUnitStudy/ClassFixtureDemo.cs new file mode 100644 index 0000000..3c3b045 --- /dev/null +++ b/xUnitStudy.Model/xUnitStudy/ClassFixtureDemo.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace xUnitStudy.Model +{ + /// + /// 测试方法共享对象实例 + /// + public class ClassFixtureDemo : IDisposable + { + public int CallTimes { get; private set; } + public List Persons; + public ClassFixtureDemo() + { + Persons = new List() + { + new Person(){ Id=1, FirstName="first1",LastName="last1" }, + new Person(){ Id=2, FirstName="first2",LastName="last2" }, + new Person(){ Id=3, FirstName="first3",LastName="last3" }, + }; + } + + public (bool result, Person person) AddPerson(Person person) + { + CallTimes += 1; + + var exist = Persons.FirstOrDefault(p => p.Id == person.Id); + if (exist == null) + { + Persons.Add(person); + return ValueTuple.Create(true, person); + } + else + { + return ValueTuple.Create(false, person); + } + } + + public (bool result, Person person) RemovePerson(Person person) + { + CallTimes += 1; + + var exist = Persons.FirstOrDefault(p => p.Id == person.Id); + if (exist == null) + { + return ValueTuple.Create(false, person); + } + else + { + Persons.Remove(exist); + return ValueTuple.Create(true, exist); + } + } + + public void Dispose() + { + Persons = null; + } + } +} diff --git a/xUnitStudy.Model/xUnitStudy/CollectionFixtureDemo.cs b/xUnitStudy.Model/xUnitStudy/CollectionFixtureDemo.cs new file mode 100644 index 0000000..7294f29 --- /dev/null +++ b/xUnitStudy.Model/xUnitStudy/CollectionFixtureDemo.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace xUnitStudy.Model +{ + /// + /// 测试方法共享对象实例 + /// + public class CollectionFixtureDemo : IDisposable + { + public List Persons; + + public CollectionFixtureDemo() + { + Persons = new List() + { + new Person(){ Id=1, FirstName="first1",LastName="last1" }, + new Person(){ Id=2, FirstName="first2",LastName="last2" }, + new Person(){ Id=3, FirstName="first3",LastName="last3" }, + }; + } + + public Person GetPersonById(int personId) + { + var find = Persons.FirstOrDefault(p => p.Id == personId); + + return find; + } + + public void Dispose() + { + Persons = null; + } + } +} diff --git a/xUnitStudy.Model/xUnitStudy/IPerson.cs b/xUnitStudy.Model/xUnitStudy/IPerson.cs new file mode 100644 index 0000000..8b7e9ef --- /dev/null +++ b/xUnitStudy.Model/xUnitStudy/IPerson.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace xUnitStudy.Model +{ + public interface IPerson + { + int Id { get; set; } + + string FirstName { get; set; } + + string LastName { get; set; } + + string GetFullName(); + } +} diff --git a/xUnitStudy.Model/xUnitStudy/Person.cs b/xUnitStudy.Model/xUnitStudy/Person.cs new file mode 100644 index 0000000..bac478e --- /dev/null +++ b/xUnitStudy.Model/xUnitStudy/Person.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace xUnitStudy.Model +{ + public class Person:IPerson,IEqualityComparer,IComparer + { + public int Id { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + + public string GetFullName() + { + return FirstName + "_" + LastName; + } + public bool Equals(Person x, Person y) + { + return x.Id == y.Id; + } + + public int GetHashCode(Person obj) + { + return obj.Id; + } + + /// + /// IComparer接口实现 + /// + /// 比较对象1 + /// 比较对象2 + /// x小于y则返回负整数;x大于y则返回正整数;x等于y则返回0 + public int Compare(Person x, Person y) + { + if (x.Id < y.Id) + { + return -1; + } + else if (x.Id == y.Id) + { + return 0; + } + else + { + return 1; + } + } + } +} diff --git a/xUnitStudy.Model/xUnitStudy/Teacher.cs b/xUnitStudy.Model/xUnitStudy/Teacher.cs new file mode 100644 index 0000000..dde6c6f --- /dev/null +++ b/xUnitStudy.Model/xUnitStudy/Teacher.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace xUnitStudy.Model +{ + public class Teacher:Person + { + public int Grade { get; set; } + } +} diff --git a/xUnitStudy.WebApi.Test/ApiControllerTest.cs b/xUnitStudy.WebApi.Test/ApiControllerTest.cs new file mode 100644 index 0000000..aea90f7 --- /dev/null +++ b/xUnitStudy.WebApi.Test/ApiControllerTest.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; +using Xunit.Abstractions; + +using xUnitStudy.Model; +using xUnitStudy.WebApi; +using xUnitStudy.WebApi.Controllers; + +namespace xUnitStudy.WebApi.Test +{ + public class ApiControllerTest + { + [Fact] + public void Test() + { + var id = 5; + var valueController = new ValuesController(); + + var result = valueController.Get(id); + + Assert.Equal(id.ToString(), result); + } + } +} diff --git a/xUnitStudy.WebApi.Test/AutoFac/AutofacStudy.cs b/xUnitStudy.WebApi.Test/AutoFac/AutofacStudy.cs new file mode 100644 index 0000000..6b2601e --- /dev/null +++ b/xUnitStudy.WebApi.Test/AutoFac/AutofacStudy.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Autofac; +using Autofac.Builder; +using Autofac.Core; +using Autofac.Extras; +using Autofac.Features; +using Autofac.Util; + +namespace xUnitStudy.WebApi.Test.AutoFac +{ + public class AutofacStudy + { + + } +} diff --git a/xUnitStudy.WebApi.Test/Properties/AssemblyInfo.cs b/xUnitStudy.WebApi.Test/Properties/AssemblyInfo.cs index a333ee7..a344d57 100644 --- a/xUnitStudy.WebApi.Test/Properties/AssemblyInfo.cs +++ b/xUnitStudy.WebApi.Test/Properties/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Xunit; // 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 @@ -34,3 +35,6 @@ using System.Runtime.InteropServices; // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] + +//xUnit 分组设置 +[assembly:AssemblyTrait("程序集分组","包括程序集内测试方法")] diff --git a/xUnitStudy.WebApi.Test/StudentBll/StudentBllTest.cs b/xUnitStudy.WebApi.Test/StudentBll/StudentBllTest.cs index d4ac5f6..1fb68b8 100644 --- a/xUnitStudy.WebApi.Test/StudentBll/StudentBllTest.cs +++ b/xUnitStudy.WebApi.Test/StudentBll/StudentBllTest.cs @@ -17,15 +17,97 @@ namespace xUnitStudy.WebApi.Test { public class StudentBllTest:IDisposable { - private StudentBll bll; + #region 准备 + private StudentBll actualBll; public StudentBllTest() { - bll = new Bll.StudentBll(); + //这里创建和设置需要注入的IDal。当然也可以创建和设置,直接注入。 + actualBll = new Bll.StudentBll(); } + #endregion [Fact] - public void GetTuitionTest() + public void Students_Test() { + IStudentBll actual_Bll = new StudentBll(); + List studentsFromProperty = actual_Bll.Students; + List studentsFromMethod = actual_Bll.GetAll(); + + Assert.Equal(studentsFromMethod, studentsFromProperty); + } + + [Fact] + public void GetAll_Test() + { + List students = actualBll.GetAll(); + + Assert.Equal(actualBll.Students, students); + } + + [Fact] + public void GetStudentById_Test() + { + var studentId = 1; + + Student student = actualBll.GetStudentById(studentId); + + Assert.NotNull(student); + Assert.Equal(studentId, student.Id); + } + + [Fact] + public void AddStudent_Test() + { + var exitsStudent = new Student() { Id=1,Name="lishi",Age=40,lever=0}; + var newStudent = new Student() { Id=100,Name="wangwu",Age=30,lever=0}; + + var exitsResult = actualBll.AddStudent(exitsStudent); + var newResult = actualBll.AddStudent(newStudent); + + Assert.False(exitsResult.result); + + Assert.True(newResult.result); + Assert.Contains(newStudent, actualBll.GetAll()); + } + + [Fact] + public void UpdateStudent_Test() + { + var exitsStudent = new Student() { Id = 1, Name = "lishi", Age = 40, lever = 0 }; + var newStudent = new Student() { Id = 100, Name = "wangwu", Age = 30, lever = 0 }; + + var exitsResult = actualBll.UpdateStudent(exitsStudent); + var newResult = actualBll.UpdateStudent(newStudent); + + Assert.True(exitsResult.result); + Assert.Contains(exitsStudent, actualBll.GetAll()); + + Assert.False(newResult.result); + } + + [Fact] + public void DeleteStudent_Test() + { + var exitsStudent = new Student() { Id = 1, Name = "lishi", Age = 40, lever = 0 }; + var newStudent = new Student() { Id = 100, Name = "wangwu", Age = 30, lever = 0 }; + + var exitsResult = actualBll.DeleteStudent(exitsStudent.Id); + var newResult = actualBll.DeleteStudent(newStudent.Id); + + Assert.True(exitsResult); + Assert.DoesNotContain(exitsStudent, actualBll.GetAll()); + + Assert.False(newResult); + } + + /// + /// 获取学费 + /// 属性注入:IDal Mock对象 + /// + [Fact] + public void GetTuition_UseMoq_Test() + { + // #准备 Mock mockStudentDal = new Mock(); mockStudentDal .Setup(m => m.GetStudentById(2)) @@ -33,18 +115,23 @@ namespace xUnitStudy.WebApi.Test ( new Student() { Id = 2, Name = "小小张", Age = 95 } ); - //属性注入 - bll.dal = mockStudentDal.Object; - var student = bll.GetStudentById(2); - var tuition = bll.GetTuition(2); + //属性注入,也可以使用构造函数注入 + actualBll.dal = mockStudentDal.Object; + + // #使用 + var student = actualBll.GetStudentById(2); + var tuition = actualBll.GetTuition(2); + // #断言 Assert.Equal(student.Id + student.Age, tuition); } + #region 清理 public void Dispose() { } + #endregion } } diff --git a/xUnitStudy.WebApi.Test/TestCaseOrderTest.cs b/xUnitStudy.WebApi.Test/TestCaseOrderTest.cs new file mode 100644 index 0000000..0fa78dc --- /dev/null +++ b/xUnitStudy.WebApi.Test/TestCaseOrderTest.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using Xunit.Sdk; + +namespace xUnitStudy.WebApi.Test +{ + /// + /// 输出窗口的测试选项,显示测试相关信息 + /// + public class TestCaseOrderTest: ITestCaseOrderer + { + private readonly IMessageSink diagnosticMessageSink; + + public TestCaseOrderTest(IMessageSink diagnosticMessageSink) + { + this.diagnosticMessageSink = diagnosticMessageSink; + } + + public IEnumerable OrderTestCases(IEnumerable testCases) where TTestCase : ITestCase + { + var result = testCases.ToList(); // Run them in discovery order + var message = new DiagnosticMessage("Ordered {0} test cases", result.Count); + diagnosticMessageSink.OnMessage(message); + return result; + } + } +} diff --git a/xUnitStudy.WebApi.Test/UseAssertTest.cs b/xUnitStudy.WebApi.Test/UseAssertTest.cs new file mode 100644 index 0000000..2d8c40a --- /dev/null +++ b/xUnitStudy.WebApi.Test/UseAssertTest.cs @@ -0,0 +1,1646 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using xUnitStudy.Model; + +namespace xUnitStudy.WebApi.Test +{ + /// + /// https://xunit.github.io/docs/comparisons + /// + [Trait("测试断言分组", "默认组")] + public class UseAssertTest : IDisposable + { + #region 准备 + public UseAssertTest() + { + + } + #endregion + + #region Equal + [Fact] + public void Equal_String_Test() + { + Assert.Equal("ping", "ping"); + } + + /// + /// 断言 string 相等 + /// 设置大小写、行尾符、空白符号 比较选项 + /// + [Fact] + public void Equal_String_Ignore_Test() + { + //ignoreCase:忽略字符大小写差异.如果为true,比较时忽略字符大小写 + Assert.Equal("ABcdEF", "abcdef", ignoreCase: true); + + //ignoreLineEndingDifferences:忽略行尾结束符差异。设置为true,则将\rn、\r和\n视为等效。 + Assert.Equal("One line\r\n", "one line\n", ignoreCase: true, ignoreLineEndingDifferences: true); + + //ignoreWhiteSpaceDifferences:忽略"空白"(空格、Tab)差异。设置为true,则将空格和制表符(以任何非零量)视为等效。 + Assert.Equal("One line\r", "one line\n", ignoreCase: true, ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true); + } + + [Fact] + public void Equal_Double_Test() + { + //注意:先舍入,然后再比较 + //舍入规则:四舍五入 + //精度范围[0,15]位小数 + + Assert.Equal(1.14d, 1.11d, precision: 1); + Assert.Equal(1.15d, 1.19d, precision: 1); + + Assert //1.14d 约等于1.1 而1.15d 约等于1.2 所以不相等 + .NotEqual(1.14d, 1.15d, precision: 1); + + //四舍五入到2位小数精度,相等 + Assert.Equal(2.123d, 2.124d, 2); + + //四舍五入到3位小数精度,相等 + Assert.Equal(3.1234d, 3.1231d, 3); + + //四舍五入到4位小数精度,不等 + Assert.NotEqual(3.12345d, 3.12349d, 4); + + //四舍五入到整数,相等 + Assert.Equal(15d, 15.111d, precision: 0); + + //没有小数位,四舍五入到整数 + Assert.Equal(15d, 15d, precision: 0); + } + + [Fact] + public void Equal_Decimal_Test() + { + //注意:先舍入,然后再比较 + //舍入规则:四舍五入 + //精度范围[0,15]位小数 + + Assert.Equal(1.14M, 1.11M, precision: 1); + Assert.Equal(1.15M, 1.19M, precision: 1); + + Assert //1.14M 约等于1.1 而1.15M 约等于1.2 所以不相等 + .NotEqual(1.14M, 1.15M, precision: 1); + + //四舍五入到2位小数精度,相等 + Assert.Equal(2.123M, 2.124M, 2); + + //四舍五入到3位小数精度,相等 + Assert.Equal(3.1234M, 3.1231M, 3); + + //四舍五入到4位小数精度,不等 + Assert.NotEqual(3.12345M, 3.12349M, 4); + + //四舍五入到整数,相等 + Assert.Equal(15M, 15.111M, precision: 0); + + //没有小数位,四舍五入到整数 + Assert.Equal(15M, 15M, precision: 0); + } + + [Fact] + public void Equal_Datetime_Test() + { + Assert.Equal(DateTime.Now, DateTime.Now.AddSeconds(1), TimeSpan.FromSeconds(2)); + Assert.Equal(DateTime.Now, DateTime.Now.AddMinutes(1), TimeSpan.FromMinutes(2)); + } + + /// + /// Equal 泛型版本 + /// 泛型版基本上都有简化写法 + /// + [Fact] + public void Equal_Generic_T_Test() + { + //形如:Assert.Equal(Expected T, Actual T); + //常见类型都可以简化,或是有更好的非泛型版,此时,一般用非泛型版 + + Assert.Equal("ping", "ping"); + Assert //基本使用非泛型版本 + .Equal("ping", "ping"); + + Assert.Equal(3, 3); + Assert //简化为 + .Equal(3, 3); + + Assert.Equal(3L, 3L); + Assert //简化为 + .Equal(3L, 3L); + + Assert.Equal(5.5d, 5.5d); + Assert //简化为 + .Equal(5.5d, 5.5d); + + Assert.Equal(5.5M, 5.5M); + Assert //简化为 + .Equal(5.5M, 5.5M); + + Assert.Equal(DateTime.Now.Date, DateTime.Now.Date); + Assert //简化为 + .Equal(DateTime.Now.Date, DateTime.Now.Date); + + //类或其它自定义结构比较,可以使用 + + var obj = new object(); + Assert.Equal(obj, obj); + Assert //简化为 + .Equal(obj, obj); + } + + /// + /// Equal 实现了IEqualityComparer接口的泛型版本 + /// + [Fact] + public void Equal_Generic_IEqualityComparer_Test() + { + //形如:Assert.Equal(Expected T, Actual T,IEqualityComparer comparer); + //实现 IEqualityComparer 接口 + + var expected = new AssertEqualDemo() { Id = 1, Name = "zhansan" }; + var actual = new AssertEqualDemo() { Id = 1, Name = "lisi" }; + + //实现 IEqualityComparer 接口的,可以是被比较对象自己实现接口,也可以是单独的类。 + var comparer = new AssertEqualDemo(); + + Assert.Equal(expected, actual, comparer); + Assert.//可以简写为 + Equal(expected, actual, comparer); + + //id不一样,则不相等 + expected.Id = 3; + Assert.NotEqual(expected, actual, comparer); + } + + /// + /// 实现IEnumerable接口对象的 Equal比较 + /// + [Fact] + public void Equal_Generic_IEnumerable_Test() + { + //对实现IEnumerable接口的对象进行 Equal比较 + //形如:Equal(IEnumerable expected, IEnumerable actual); + + List expected = new List() { "first", "second" }; + List actual = new List() { "first", "second" }; + + Assert.Equal>(expected, actual); + Assert.//简写为 + Equal(expected, actual); + } + + /// + /// 实现IEnumerable接口对象的 Equal 比较 + /// 传入实现IEqualityComparer接口的比较器 + /// + [Fact] + public void Equal_Generic_IEnumerable_IEqualityComparer_Test() + { + //对实现IEnumerable接口的对象进行 Equal比较 + //形如:Equal(IEnumerable expected, IEnumerable actual,IEqualityComparer comparer) + //传入实现了 IEqualityComparer 接口的比较参数 + + var expected = new List() + { + new AssertEqualDemo() { Id = 1, Name = "zhansan" }, + new AssertEqualDemo() { Id = 2, Name = "lis" }, + }; + var actual = new List() + { + new AssertEqualDemo() { Id = 1, Name = "wangwu" }, + new AssertEqualDemo() { Id = 2, Name = "zhaoliu" }, + }; + + //实现 IEqualityComparer 接口的,可以是被比较对象自己实现接口,也可以是单独的类。 + var comparer = new AssertEqualDemo(); + + Assert.Equal(expected, actual, comparer); + Assert.//可以简写为 + Equal(expected, actual, comparer); + + //id不一样,则不相等 + expected[0].Id = 100; + Assert.NotEqual(expected, actual, comparer); + } + #endregion + + #region NotEqual + [Fact] + public void NotEqual_String_Test() + { + Assert.NotEqual("ping", "ack"); + } + + [Fact] + public void NotEqual_Double_Test() + { + //注意:先舍入,然后再比较 + //舍入规则:四舍五入 + //精度范围[0,15]位小数 + + Assert //1.14d 约等于1.1 而1.15d 约等于1.2 所以不相等 + .NotEqual(1.14d, 1.15d, precision: 1); + + //舍入到2位小数精度,相等 + Assert.NotEqual(2.124d, 2.126d, 2); + + //舍入到3位小数精度,相等 + Assert.NotEqual(3.1234d, 3.1239d, 3); + + //舍入到4位小数精度,不等 + Assert.NotEqual(3.12346d, 3.12341d, 4); + + //舍入到整数,相等 + Assert.NotEqual(16d, 15.111d, precision: 0); + + //没有小数位,舍入到整数 + Assert.NotEqual(16d, 160d, precision: 0); + } + + [Fact] + public void NotEqual_Decimal_Test() + { + //注意:先舍入,然后再比较 + //舍入规则:四舍五入 + //精度范围[0,15]位小数 + + Assert //1.14M 约等于1.1 而1.15M 约等于1.2 所以不相等 + .NotEqual(1.14M, 1.15M, precision: 1); + + //四舍五入到2位小数精度,不等 + Assert.NotEqual(2.123M, 2.129M, 2); + + //四舍五入到3位小数精度,不等 + Assert.NotEqual(3.1234M, 3.1236M, 3); + + //四舍五入到4位小数精度,不等 + Assert.NotEqual(3.12345M, 3.12349M, 4); + + //四舍五入到整数,不等 + Assert.NotEqual(15M, 15.501M, precision: 0); + + //没有小数位,四舍五入到整数 + Assert.NotEqual(29M, 15M, precision: 0); + } + + [Fact] + public void NotEqual_Generic_T_Test() + { + //形如:Assert.NotEqual(Expected T, Actual T); + + Assert.NotEqual("ping", "ack"); + Assert //基本使用非泛型版本 + .NotEqual("ping", "ack"); + + Assert.NotEqual(3, 4); + Assert //简化为 + .NotEqual(3, 4); + + Assert.NotEqual(3L, 4L); + Assert //简化为 + .NotEqual(3L, 4L); + + Assert.NotEqual(5.5d, 6.5d); + Assert //简化为 + .NotEqual(5.5d, 6.5d); + + Assert.NotEqual(5.5M, 5.6M); + Assert //简化为 + .NotEqual(5.5M, 6.5M); + + Assert.NotEqual(DateTime.Now.Date, DateTime.Now.Date.AddDays(1)); + Assert //简化为 + .NotEqual(DateTime.Now.Date, DateTime.Now.Date.AddDays(1)); + + //类或其它自定义结构比较,可以使用 + + var obj = new object(); + var obj2 = new object(); + Assert.NotEqual(obj, obj2); + Assert //简化为 + .NotEqual(obj, obj2); + } + + [Fact] + public void NotEqual_Generic_IEqualityComparer_Test() + { + //形如:Assert.NotEqual(Expected T, Actual T,IEqualityComparer comparer); + //实现 IEqualityComparer 接口 + + var expected = new AssertEqualDemo() { Id = 1, Name = "zhansan" }; + var actual = new AssertEqualDemo() { Id = 1, Name = "lishi" }; + + //实现 IEqualityComparer 接口的,可以是被比较对象自己实现接口,也可以是单独的类。 + var comparer = new AssertEqualDemo(); + + Assert.Equal(expected, actual, comparer); + Assert.//可以简写为 + Equal(expected, actual, comparer); + + //id不一样,则不相等 + expected.Id = 3; + Assert.NotEqual(expected, actual, comparer); + } + + [Fact] + public void NotEqual_Generic_IEnumerable_Test() + { + //对实现IEnumerable接口的对象进行 Equal比较 + //形如:NotEqual(IEnumerable expected, IEnumerable actual) + + List expected = new List() { "first", "second", "third" }; + List actual = new List() { "first", "second" }; + + Assert.NotEqual>(expected, actual); + Assert.//简写为 + NotEqual(expected, actual); + } + + [Fact] + public void NotEqual_Generic_IEnumerable_IEqualityComparer_Test() + { + //对实现IEnumerable接口的对象进行 Equal比较 + //形如:NotEqual(IEnumerable expected, IEnumerable actual,IEqualityComparer comparer) + //传入实现了 IEqualityComparer 接口的比较参数 + + var expected = new List() + { + new AssertEqualDemo() { Id = 1, Name = "zhansan" }, + new AssertEqualDemo() { Id = 2, Name = "lis" }, + }; + var actual = new List() + { + new AssertEqualDemo() { Id = 1, Name = "wangwu" }, + new AssertEqualDemo() { Id = 2, Name = "zhaoliu" }, + }; + + //实现 IEqualityComparer 接口的,可以是被比较对象自己实现接口,也可以是单独的类。 + var comparer = new AssertEqualDemo(); + + Assert.Equal(expected, actual, comparer); + Assert.//可以简写为 + Equal(expected, actual, comparer); + + //id不一样,则不相等 + expected[0].Id = 100; + Assert.NotEqual(expected, actual, comparer); + } + #endregion + + #region StrictEqual + [Fact] + public void StrictEqual_Test() + { + //使用类型的默认比较器验证两个对象是否严格相等,用法类似Equal + //不支持自定义IEqualityComparer + Assert.StrictEqual(1L, 1m); + + + Person person1 = new Person() { Id = 2 }; + Person person2 = new Person() { Id = 2 }; + + Person person3 = new Person() { Id = 1 }; + IPerson person4 = person3; + + Assert.StrictEqual(person3, person4); + } + + [Fact] + public void NotStrictEqual_Test() + { + //使用类型的默认比较器验证两个对象是否严格相等,用法类似NotEqual + //不支持自定义IEqualityComparer + Assert.NotStrictEqual(2.55M, 3.55M); + + + Person person1 = new Person() { Id = 2 }; + Person person2 = new Person() { Id = 2 }; + + Person person3 = new Person() { Id = 1 }; + IPerson person4 = person1; + + Assert.NotStrictEqual(person1, person2); + } + #endregion + + #region Same + [Fact] + public void Same_String_Test() + { + //验证两个引用类型的对象,是同一个实例 + //注意:必须是引用类型,不能是值类型 + + //特别注明:sting类型,虽然是引用类型,但是比较特殊。采取复制策略,相同值可以(不严谨的说法)认为引用相同 + string expected = "hell,world!"; + string actual = "hell,world!"; + Assert.Same(expected, actual); + + string expected2 = "hell,world!"; + string actual2 = expected2; + Assert.Same(expected2, actual2); + } + + [Fact] + public void Same_Object_Test() + { + object obj1 = new object(); + object obj2 = new object(); + object obj3 = obj1; + object obj4 = obj2; + + Assert.Same(obj1, obj3); + Assert.Same(obj2, obj4); + } + + [Fact] + public void Same_Class_Test() + { + object obj1 = new AssertEqualDemo() { Id = 1, Name = "zhangsan" }; + object obj2 = new AssertEqualDemo() { Id = 1, Name = "zhangsan" }; + object obj3 = obj1; + object obj4 = obj2; + + Assert.Same(obj1, obj3); + Assert.Same(obj2, obj4); + } + #endregion + + #region NotSame + [Fact] + public void NotSame_String_Test() + { + //验证两个引用类型的对象,是同一个实例 + //注意:必须是引用类型,不能是值类型 + + //特别注明:sting类型,虽然是引用类型,但是比较特殊。采取复制策略,相同值可以(不严谨的说法)认为引用相同 + string expected = "hell,world!"; + string actual = "I am chinese!"; + Assert.NotSame(expected, actual); + + Assert.NotSame("abc", "xyz"); + } + + [Fact] + public void NotSame_Object_Test() + { + object obj1 = new object(); + object obj2 = new object(); + Object obj3 = obj2; + + Assert.NotSame(obj1, obj2); + Assert.NotSame(obj1, obj3); + Assert.NotSame(new object(), new object()); + } + + [Fact] + public void NotSame_Class_Test() + { + object obj1 = new AssertEqualDemo() { Id = 1, Name = "zhangsan" }; + object obj2 = new AssertEqualDemo() { Id = 1, Name = "zhangsan" }; + object obj3 = obj1; + object obj4 = obj2; + + Assert.NotSame(obj1, obj2); + Assert.NotSame(obj3, obj4); + Assert.NotSame(obj1, obj4); + Assert.NotSame(obj2, obj3); + } + #endregion + + #region True + [Fact] + public void True_Condition_Test() + { + Assert.True(true); + Assert.True("ping" == "ping"); + Assert.True(2 == 1 + 1); + Assert.True("ping" == "ping" && 2 == 1 + 1); + } + + [Fact] + public void True_Nullable_Test() + { + bool? expected = null; + Assert.True(!expected.HasValue); + + int? expected2 = null; + Assert.True(expected2 == null); + expected2 = 2; + Assert.True(2 == expected2.Value); + } + + [Fact] + public void True_Message_Test() + { + Assert.True(true, "userMessage"); + Assert.True("ping" == "ping", "userMessage"); + Assert.True(2 == 1 + 1, "userMessage"); + + int? nullableInt = null; + Assert.True(null == nullableInt, "userMessage"); + } + #endregion + + #region False + [Fact] + public void False_Condition_Test() + { + Assert.False(false); + Assert.False("ping" == "ack"); + Assert.False(3 == 1 + 1); + Assert.False("ping" == "ping" && 3 == 1 + 1); + } + + [Fact] + public void False_Nullable_Test() + { + bool? expected = null; + Assert.False(expected.HasValue); + + int? expected2 = 3; + Assert.False(expected2 == null); + Assert.False(4 == expected2.Value); + } + + [Fact] + public void False_Message_Test() + { + Assert.False(false, "userMessage"); + Assert.False("ping" == "ack", "userMessage"); + Assert.False(2 == 1 + 3, "userMessage"); + + int? nullableInt = 3; + Assert.False(null == nullableInt, "userMessage"); + } + #endregion + + #region True|False 可替代断言 + /// + /// 直接设置验证通过 + /// + [Fact] + public void Pass_Test() + { + Assert.True(true, "用True断言替代"); + } + + [Fact] + public void Fail_Test() + { + //Assert.True(false, "用True断言替代"); + } + + /// + /// 大于 + /// + [Fact] + public void GreaterThan_Test() + { + Assert //大于比较,用True(x > y) 替代 + .True(2 > 1); + + int max = 5; + int min = 2; + + Assert.True(max > min); + } + + /// + /// 小于 + /// + [Fact] + public void LessThan_Test() + { + Assert //小于比较,用True(x < y) 替代 + .True(2 < 3); + + int max = 5; + int min = 2; + + Assert.True(min < max); + } + + [Fact] + public void NaN_Test() + { + //NaN=Not a Number,是否不为数字 + + //说明:在浮点数计算中,0除以0将得到NaN,正数除以0将得到PositiveInfinity,负数除以0将得到NegativeInfinity。 + //浮点数运算从不引发异常 + + Assert.True(double.IsNaN(0 / 0d)); + + Assert.True(double.IsPositiveInfinity(1 / 0d)); + + Assert.True(double.IsNegativeInfinity(-1 / 0d)); + + } + #endregion + + #region Null|Empty + [Fact] + public void Empty_Test() + { + List expected = new List(); + + Assert //针对于集合,为Empty,即是集合中元素的个数为0。但集合为空引用null值时将引发异常 + .Empty(expected); + } + + [Fact] + public void NotEmpty_Test() + { + List expected = new List() { "first", "second" }; + + Assert //针对于集合,不为Empty,即是集合中元素的个数大于0。集合为空引用null值时将引发异常 + .NotEmpty(expected); + } + + [Fact] + public void Null_Test() + { + Assert //null值,空引用 + .Null(null); + + string exp = null; + Assert.Null(exp); + + List list = null; + Assert.Null(list); + } + + [Fact] + public void NotNull_Test() + { + Assert //null值,空引用 + .NotNull(new object()); + + Assert.NotNull(""); + Assert.NotNull(string.Empty); + + Assert.NotNull(new List()); + + Assert.NotNull(new List() { "first", "second" }); + } + #endregion + + #region Type|Assignable + [Fact] + public void IsType_Test() + { + //此形式,只在泛型不能使用时,才使用;应优先使用泛型版。 + Assert.IsType(typeof(string), "aaaa"); + + //泛型版 + Assert.IsType("hell,world!"); + Assert.IsType(2); + Assert.IsType(2L); + Assert.IsType(2d); + Assert.IsType(2M); + Assert.IsType(DateTime.Now); + Assert.IsType(new AssertEqualDemo()); + } + + [Fact] + public void IsNotType_Test() + { + //此形式,只在泛型不能使用时,才使用;应优先使用泛型版。 + Assert.IsNotType(typeof(string), 22); + + //泛型版 + Assert.IsNotType(2); + Assert.IsNotType("hello"); + Assert.IsNotType("521"); + Assert.IsNotType(new object()); + Assert.IsNotType("55555"); + Assert.IsNotType("2018-08-03"); + Assert.IsNotType(new object()); + } + + [Fact] + public void IsAssignableFrom_Test() + { + //可以从指定的类型派生 + Assert.IsAssignableFrom(22); + + + Person person = new Person() { Id = 1, FirstName = "first", LastName = "last" }; + Assert. //接口派生 + IsAssignableFrom(person); + + Teacher teacher = new Teacher() { Id = 1, FirstName = "first", LastName = "last", Grade = 2 }; + Assert. //类继承 + IsAssignableFrom(teacher); + + Assert //接口派生 + .IsAssignableFrom(teacher); + + + Assert //万源归宗 + .IsAssignableFrom(teacher); + } + + /// + /// 断言非派生、继承关系 + /// Assert.False(obj is Type)替代 + /// + [Fact] + public void IsNotAssignableFrom_Test() + { + // Assert.False(obj is Type) 替代 + + //可以从指定的类型派生 + Assert.False(88 is String); + Assert.False(new object() is int); + + Person person = new Person() { Id = 1, FirstName = "first", LastName = "last" }; + Teacher teacher = new Teacher() { Id = 2, FirstName = "first", LastName = "last", Grade = 2 }; + IPerson person2 = new Person() { Id = 3, FirstName = "first", LastName = "last" }; + + Assert.False(person is Teacher); + Assert.False(person2 is Teacher); + } + #endregion + + #region Contains For String + [Fact] + public void Contains_String_Test() + { + Assert.Contains("hello", "hello,world."); + Assert.Contains("llo", "hello,world."); + Assert.Contains("world", "hello,world."); + } + + [Fact] + public void Contains_String_Comparison_Ordinal_Test() + { + //StringComparison.Ordinal 二进制比较:最快,最严格 + //进行非语言(non-linguistic)上的比较,对两个字符串进行byte级别的比较,比较结果严格而准确,性能非常好。 + Assert.Contains("hello", "hello,world", StringComparison.Ordinal); + Assert.Contains("llo", "hello,world", StringComparison.Ordinal); + Assert.Contains("orld", "hello,world", StringComparison.Ordinal); + } + + [Fact] + public void Contains_String_Comparison_OrdinalIgnoreCase_Test() + { + //StringComparison.OrdinalIgnoreCase与StringComparison.Ordinal相同,只是不区分大小写 + Assert.Contains("hello", "Hello,World", StringComparison.OrdinalIgnoreCase); + Assert.Contains("llo", "Hello,World", StringComparison.OrdinalIgnoreCase); + Assert.Contains("orld", "Hello,World", StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public void Contains_String_Comparison_CurrentCulture_Test() + { + //StringComparison.CurrentCulture 默认的比较方式:使用区域敏感排序规则和当前区域比较字符串 + //在当前的区域信息下进行比较,这是String.Compare在没有指定StringComparison的时候默认的比较方式 + + Assert.Contains("", "", StringComparison.CurrentCulture); + Assert.Contains("orl", "hello,world", StringComparison.CurrentCulture); + + //设置特定区域 + string s1 = "encyclopædia"; + string s2 = "encyclopaedia"; + + //当前的区域信息是美国:s1与s2是相等的。 + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + Assert.Contains(s1, s2, StringComparison.CurrentCulture); + + //当前的区域信息是se-SE,s1与s2是相等的 + Thread.CurrentThread.CurrentCulture = new CultureInfo("se-SE"); + Assert.DoesNotContain(s1, s2, StringComparison.CurrentCulture); + } + + [Fact] + public void Contains_String_Comparison_CurrentCultureIgnoreCase_Test() + { + //用法同:StringComparison.CurrentCulture,只是不区分大小写 + + Assert.Contains("A", "aBB", StringComparison.CurrentCultureIgnoreCase); + Assert.Contains("worl", "Hello,World", StringComparison.CurrentCultureIgnoreCase); + + //设置特定区域 + string s1 = "Encyclopædia"; + string s2 = "encyclopaediA"; + + //当前的区域信息是美国:s1与s2是相等的。 + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + Assert.Contains(s1, s2, StringComparison.CurrentCultureIgnoreCase); + + //当前的区域信息是se-SE,s1与s2是相等的 + Thread.CurrentThread.CurrentCulture = new CultureInfo("se-SE"); + Assert.DoesNotContain(s1, s2, StringComparison.CurrentCultureIgnoreCase); + } + + [Fact] + public void Contains_String_Comparison_InvariantCulture_Test() + { + //StringComparison.InvariantCulture:使用区域敏感排序规则和固定区域比较字符串 + //在任何系统中(不同的culture)比较都将得到相同的结果,他是使用CultureInfo.InvariantCulture的静态成员CompareInfo来进行比较操作的. + //StringComparison.InvariantCultureIgnoreCase与StringComparison.InvariantCulture,只是忽略大小写 + + Assert.Contains("ping", "ping", StringComparison.InvariantCulture); + Assert.Contains("p", "ping", StringComparison.InvariantCulture); + Assert.Contains("in", "ping", StringComparison.InvariantCulture); + Assert.Contains("ing", "ping", StringComparison.InvariantCulture); + + //区域无关性 + string s1 = "encyclopædia"; + string s2 = "encyclopaedia"; + + Assert.Contains(s1, s2, StringComparison.InvariantCulture); + + //设置区为美国:无效 + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + Assert.Contains(s1, s2, StringComparison.InvariantCulture); + + //设置区为se-SE:无效 + Thread.CurrentThread.CurrentCulture = new CultureInfo("se-SE"); + Assert.Contains(s1, s2, StringComparison.InvariantCulture); + } + + [Fact] + public void Contains_String_Comparison_InvariantCultureIgnoreCase_Test() + { + //StringComparison.InvariantCultureIgnoreCase与StringComparison.InvariantCulture,只是忽略大小写 + + Assert.Contains("ping", "PinG", StringComparison.InvariantCultureIgnoreCase); + Assert.Contains("p", "PinG", StringComparison.InvariantCultureIgnoreCase); + Assert.Contains("in", "PinG", StringComparison.InvariantCultureIgnoreCase); + Assert.Contains("ing", "PinG", StringComparison.InvariantCultureIgnoreCase); + + //区域无关性 + string s1 = "Encyclopædia"; + string s2 = "encyclopaediA"; + + Assert.Contains(s1, s2, StringComparison.InvariantCultureIgnoreCase); + + //设置区为美国:无效 + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + Assert.Contains(s1, s2, StringComparison.InvariantCultureIgnoreCase); + + //设置区为se-SE:无效 + Thread.CurrentThread.CurrentCulture = new CultureInfo("se-SE"); + Assert.Contains(s1, s2, StringComparison.InvariantCultureIgnoreCase); + } + #endregion + + #region Contains Collection + [Fact] + public void Contains_Generic_In_IEnumerable_Test() + { + //字符数组 + var stringArray = new List() { "first", "second", "third", "four" }; + Assert.Contains("first", stringArray); + Assert //可以简化写法 + .Contains("second", stringArray); + + //int 数组 + var intArray = new List() { 1, 2, 3, 4, 5 }; + Assert.Contains(2, intArray); + Assert //可以简化写法 + .Contains(3, intArray); + + //类组 + + //内部各项都相等,但实例(引用)不同,则不包含 + var classArray = new List() + { + new Person(){ Id=1, FirstName="first1", LastName="last1"}, + new Person(){ Id=2, FirstName="first2", LastName="last2"}, + new Person(){ Id=3, FirstName="first3", LastName="last3"}, + }; + + Person person1 = new Person() { Id = 1, FirstName = "first1", LastName = "last1" }; + Person person2 = new Person() { Id = 2, FirstName = "first2", LastName = "last2" }; + Person person3 = new Person() { Id = 3, FirstName = "first3", LastName = "last3" }; + + Assert.DoesNotContain(person1, classArray); + Assert //简化写法 + .DoesNotContain(person2, classArray); + Assert //简化写法 + .DoesNotContain(person3, classArray); + + //相同的引用才包含 + classArray.Add(person1); + classArray.Add(person2); + classArray.Add(person3); + + Assert.Contains(person1, classArray); + Assert.Contains(person2, classArray); + Assert.Contains(person3, classArray); + + } + + [Fact] + public void Contains_Generic_In_IEnumerable_IEqualityComparer_Test() + { + //以 IEqualityComparer 比较器为断言依据 + + Person person1 = new Person() { Id = 1, FirstName = "first1", LastName = "last1" }; + Person person2 = new Person() { Id = 2, FirstName = "first2", LastName = "last2" }; + Person person3 = new Person() { Id = 3, FirstName = "first3", LastName = "last3" }; + + var classArray = new List() + { + new Person() { Id = 1, FirstName = "ab", LastName = "cd" }, + new Person() { Id = 2, FirstName = "ef", LastName = "g" }, + new Person() { Id = 3, FirstName = "s", LastName = "b" }, + }; + + //自定义比较器 + IEqualityComparer equalityComparer = new Person(); + + Assert.Contains(person1, classArray, equalityComparer); + Assert.Contains(person2, classArray, equalityComparer); + Assert.Contains(person3, classArray, equalityComparer); + } + + [Fact] + public void Contains_Generic_IEnumerable_Filter_Test() + { + Person person1 = new Person() { Id = 1, FirstName = "first1", LastName = "last1" }; + Person person2 = new Person() { Id = 2, FirstName = "first2", LastName = "last2" }; + Person person3 = new Person() { Id = 3, FirstName = "first3", LastName = "last3" }; + + var classArray = new List() + { + person1, + person2, + person3, + }; + + //Predicate 委托(入参T,返回 bool),作为筛选条件 + Assert.Contains(classArray, person => person.Id == 1); + Assert //简写为 + .Contains(classArray, person => person.FirstName == "first1"); + Assert //简写为 + .Contains(classArray, person => person.Id == 3 && person.LastName == "last3"); + } + + [Fact] + public void Contains_Generic_In_IDictionary_Test() + { + //字符串 + KeyValuePair sring_kv1 = new KeyValuePair("key1", "first"); + KeyValuePair string_kv2 = new KeyValuePair("key2", "second"); + KeyValuePair string_kv3 = new KeyValuePair("key3", "third"); + + IDictionary stringDic = new Dictionary(); + stringDic.Add("key1", "first"); + stringDic.Add("key2", "second"); + stringDic.Add("key3", "third"); + + Assert.Contains("key1", stringDic); + Assert.Contains>(sring_kv1, stringDic); + + //数字 + KeyValuePair int_kv1 = new KeyValuePair("key1", 1); + KeyValuePair int_kv2 = new KeyValuePair("key2", 2); + KeyValuePair int_kv3 = new KeyValuePair("key3", 3); + + IDictionary intDic = new Dictionary(); + intDic.Add("key1", 1); + intDic.Add("key2", 2); + intDic.Add("key3", 3); + + Assert.Contains("key1", intDic); + Assert.Contains>(int_kv1, intDic); + + //类 + Person person1 = new Person() { Id = 1, FirstName = "first1", LastName = "last1" }; + Person person2 = new Person() { Id = 2, FirstName = "first2", LastName = "last2" }; + Person person3 = new Person() { Id = 3, FirstName = "first3", LastName = "last3" }; + + KeyValuePair class_kv1 = new KeyValuePair("key1", person1); + KeyValuePair class_kv2 = new KeyValuePair("key2", person2); + KeyValuePair class_kv3 = new KeyValuePair("key3", person3); + + IDictionary classDic = new Dictionary(); + classDic.Add("key1", person1); + classDic.Add("key2", person2); + classDic.Add("key3", person3); + + Assert.Contains("key1", classDic); + Assert.Contains>(class_kv1, classDic); + Assert.Contains(class_kv2, classDic); + Assert.Contains(class_kv3, classDic); + } + + [Fact] + public void Contains_Generic_In_IReadOnlyDictionary_Test() + { + //类似 IDictionary + + //字符串 + KeyValuePair sring_kv1 = new KeyValuePair("key1", "first"); + KeyValuePair string_kv2 = new KeyValuePair("key2", "second"); + KeyValuePair string_kv3 = new KeyValuePair("key3", "third"); + + IDictionary stringDic = new Dictionary(); + stringDic.Add("key1", "first"); + stringDic.Add("key2", "second"); + stringDic.Add("key3", "third"); + + IReadOnlyDictionary stringReadOnlyDic = stringDic as IReadOnlyDictionary; + + Assert.Contains("key1", stringReadOnlyDic); + Assert.Contains>(sring_kv1, stringReadOnlyDic); + + //类 + Person person1 = new Person() { Id = 1, FirstName = "first1", LastName = "last1" }; + Person person2 = new Person() { Id = 2, FirstName = "first2", LastName = "last2" }; + Person person3 = new Person() { Id = 3, FirstName = "first3", LastName = "last3" }; + + KeyValuePair class_kv1 = new KeyValuePair("key1", person1); + KeyValuePair class_kv2 = new KeyValuePair("key2", person2); + KeyValuePair class_kv3 = new KeyValuePair("key3", person3); + + IDictionary classDic = new Dictionary(); + classDic.Add("key1", person1); + classDic.Add("key2", person2); + classDic.Add("key3", person3); + + IReadOnlyDictionary classReadOnlyDic = classDic as IReadOnlyDictionary; + + Assert.Contains("key1", classReadOnlyDic); + Assert.Contains>(class_kv1, classReadOnlyDic); + Assert.Contains(class_kv2, classReadOnlyDic); + Assert.Contains(class_kv3, classReadOnlyDic); + + } + #endregion + + #region DoesNotContain 类似Contain 略过 + #endregion + + #region InRange + + [Fact] + public void InRange_Test() + { + Assert.InRange(5, 1, 10); + Assert.InRange("b", "a", "d"); + } + + [Fact] + public void InRange_IComparable_Test() + { + IComparer comparer = new Person(); + + Person person1 = new Person() { Id = 1 }; + Person person2 = new Person() { Id = 2 }; + Person person3 = new Person() { Id = 3 }; + + Assert.InRange(person2, person1, person3, comparer); + } + + #endregion + + #region NotInRange + [Fact] + public void NotInRange_Test() + { + Assert.NotInRange(5, 10, 100); + Assert.NotInRange(20, 100, 2000); + Assert.NotInRange("a", "b", "d"); + Assert.NotInRange("z", "b", "d"); + } + + [Fact] + public void NotInRange_IComparable_Test() + { + IComparer comparer = new Person(); + + Person person1 = new Person() { Id = 1 }; + Person person2 = new Person() { Id = 2 }; + Person person3 = new Person() { Id = 3 }; + + Assert.NotInRange(person1, person2, person3, comparer); + } + #endregion + + #region Throws + [Fact] + public void Throws_Func_Test() + { + //方法不推荐使用:用泛型版本替代 + var result = Assert.Throws(typeof(ArgumentNullException), () => { ArgumentNullException ex = new ArgumentNullException("userName"); throw ex; return ex; }); + } + + [Fact] + public void Throws_Action_Test() + { + //方法不推荐使用:用泛型版本替代 + var result = Assert.Throws(typeof(FormatException), () => decimal.Parse("abc")); + } + + [Fact] + public async Task Throws_Async_Test() + { + //方法不推荐使用:用泛型版本替代 + var result = await Assert.ThrowsAsync(typeof(FormatException), () => { return Task.Run(() => { int.Parse("abc"); }); }); + } + + [Fact] + public void Throws_Generic_Func_Test() + { + var result = Assert.Throws(() => ThrowAndReturnFormatException()); + } + + [Fact] + public void Throws_Generic_Action_Test() + { + var result = Assert.Throws(() => ThrowFormatException()); + } + + [Fact] + public void Throws_Generic_ParamName_Func_Test() + { + var paramName = "userName"; + var result = Assert.Throws(paramName, () => ThrowAndReturnArgumentNullException(paramName)); + } + + [Fact] + public void Throws_Generic_ParamName_Action_Test() + { + var paramName = "userName"; + var result = Assert.Throws(paramName, () => ThrowArgumentNullException(paramName)); + } + + [Fact] + public async Task Throws_Generic_Func_Async_Test() + { + var reslut = await Assert.ThrowsAsync(() => ThrowAndReturnFormatExceptionAsync()); + } + + [Fact] + public async Task Throws_Generic_ParamName_Async_Test() + { + var paramName = "userName"; + var reslut = await Assert.ThrowsAsync(paramName, () => ThrowAndReturnArgumentNullExceptionAsync(paramName)); + } + + [Fact] + public void ThrowsAny_Func_Test() + { + var result = Assert.ThrowsAny(() => ThrowAndReturnFormatException()); + } + + /// + /// 断言异常或派生类异常 + /// + [Fact] + public void ThrowsAny_Action_Test() + { + var result = Assert.ThrowsAny(() => ThrowFormatException()); + } + + [Fact] + public async Task ThrowsAny_Func_Async_Test() + { + var result = await Assert.ThrowsAnyAsync(() => ThrowAndReturnFormatExceptionAsync()); + } + + [Fact] + public void NotThrow_Exception_Test() + { + //2.0及更高版本,移除了 NotThrow断言 + //使用TryCatch替代或自定断言扩展 + + Exception exception = null; + try + { + //dosomething + exception = null; + } + catch (Exception ex) + { + exception = ex; + } + + Assert.Null(exception); + + } + #endregion + + #region Matches + [Fact] + public void Matches_String_Test() + { + //用户名正则:大小写字线、数字、下划线组成,6-16位长度 + var userNameReg = @"^[a-zA-Z]\w{5,15}$"; + Assert.Matches(userNameReg, "bicijinlian"); + Assert.Matches(userNameReg, "wangerxiao_1981"); + + //email正则 + var emailReg = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"; + Assert.Matches(emailReg, "bicijinlian@163.com"); + Assert.Matches(emailReg, "wangerxiao_1981@126.com"); + Assert.Matches(emailReg, "15601716045@126.com"); + } + + [Fact] + public void DoesNotMatch_String_Test() + { + //用户名正则:大小写字线、数字、下划线组成,6-16位长度 + var userNameReg = @"^[a-zA-Z]\w{5,15}$"; + Assert.DoesNotMatch(userNameReg, "abcd0123456789abcdefg"); + Assert.DoesNotMatch(userNameReg, "wang"); + Assert.DoesNotMatch(userNameReg, "bicijinlian$"); + Assert.DoesNotMatch(userNameReg, "wangerxiao_1981@163"); + + //email正则 + var emailReg = @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"; + Assert.DoesNotMatch(emailReg, "bicijinlian"); + Assert.DoesNotMatch(emailReg, "wangerxiao_1981@126"); + Assert.DoesNotMatch(emailReg, "15601716045126.com"); + } + + [Fact] + public void Matches_Regex_Test() + { + //用户名正则:大小写字线、数字、下划线组成,6-16位长度 + var userNameReg = new System.Text.RegularExpressions.Regex(@"^[a-zA-Z]\w{5,15}$"); + Assert.Matches(userNameReg, "bicijinlian"); + Assert.Matches(userNameReg, "wangerxiao_1981"); + + //email正则 + var emailReg = new System.Text.RegularExpressions.Regex(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"); + Assert.Matches(emailReg, "bicijinlian@163.com"); + Assert.Matches(emailReg, "wangerxiao_1981@126.com"); + Assert.Matches(emailReg, "15601716045@126.com"); + } + + [Fact] + public void DoesNotMatch_Regex_Test() + { + //用户名正则:大小写字线、数字、下划线组成,6-16位长度 + var userNameReg = new System.Text.RegularExpressions.Regex(@"^[a-zA-Z]\w{5,15}$"); + Assert.DoesNotMatch(userNameReg, "abcd0123456789abcdefg"); + Assert.DoesNotMatch(userNameReg, "wang"); + Assert.DoesNotMatch(userNameReg, "bicijinlian$"); + Assert.DoesNotMatch(userNameReg, "wangerxiao_1981@163"); + + //email正则 + var emailReg = new System.Text.RegularExpressions.Regex(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"); + Assert.DoesNotMatch(emailReg, "bicijinlian"); + Assert.DoesNotMatch(emailReg, "wangerxiao_1981@126"); + Assert.DoesNotMatch(emailReg, "15601716045126.com"); + } + #endregion + + #region StartWith | EndWith + [Fact] + public void StartsWith_Test() + { + Assert.StartsWith("Start", "StartAndEnd"); + Assert.StartsWith("start", "StartAndEnd", StringComparison.CurrentCultureIgnoreCase); + } + + [Fact] + public void EndsWith_Test() + { + Assert.EndsWith("end", "startAndend"); + Assert.EndsWith("end", "StartAndEnd", StringComparison.CurrentCultureIgnoreCase); + } + + #endregion + + #region Single + [Fact] + public void Single_Collection_Test() + { + //只有一项的集合 + + List stringArray = new List() { "first" }; + Assert.Single(stringArray); + + List decimalArray = new List() { 5.66M }; + Assert.Single(decimalArray); + + List classArray = new List() { new Person() { Id = 2 } }; + Assert.Single(classArray); + } + + /// + /// 集合中,指定项没有重复项 + /// + [Fact] + public void Single_Collection_Expected_Test() + { + //集合中,指定的项只有一个,即是指定项在集合中没有重复项 + //不被指定的项,可以有多个 + + List stringArray = new List() { "first", "second", "second" }; + + Assert //如果换成:List stringArray = new List() { "first","second","first" } + //因为"first"有两项,则断言失败 + .Single(stringArray, "first"); + + List decimalArray = new List() { 5.66M, 7.7M, 8.8M }; + Assert.Single(decimalArray, 5.66M); + + Person person1 = new Person() { Id = 1 }; + Person person2 = new Person() { Id = 2 }; + Person person3 = new Person() { Id = 2 }; + List classArray = new List() { person1, person2 }; + Assert.Single(classArray, person1); + + List classArray2 = new List() { person1, person2, person3 }; + Assert.True(true, "person2和person3是相同的项,所以 Assert.Single(classArray, person2) 会失败"); + } + + [Fact] + public void Single_Generic_Test() + { + //只有一项的集合(推荐简化写法) + + List stringArray = new List() { "first" }; + Assert.Single(stringArray); + + List decimalArray = new List() { 5.66M }; + Assert.Single(decimalArray); + + List classArray = new List() { new Person() { Id = 2 } }; + Assert.Single(classArray); + } + + [Fact] + public void Single_Generic_Predicate_Test() + { + //集合中,指定表达式的项只有一个,即是Predicate<>筛选结果在集合中没有重复项 + //不被指定的项,可以有多个 + + List stringArray = new List() { "first", "second", "second" }; + + Assert //如果换成:List stringArray = new List() { "first","second","first" } + //因为"first"有两项,则断言失败 + .Single(stringArray, s => s.StartsWith("f")); + + List decimalArray = new List() { 5.66M, 7.7M, 8.8M }; + Assert.Single(decimalArray, d => d > 8); + + Person person1 = new Person() { Id = 1 }; + Person person2 = new Person() { Id = 2 }; + Person person3 = new Person() { Id = 2 }; + + List classArray = new List() { person1, person2, person3 }; + + Assert.Single(classArray, p => p.Id == 1); + + //Assert.Single(classArray, p => p.Id == 2),则失败 + } + + #endregion + + [Fact] + public void All_Test() + { + List items = new List() { "abc", "ac", "ad", "adddeddd" }; + Assert.All(items, f => f.StartsWith("a")); + } + + /// + /// 相当于集合的自定义断言,实用意义不大 + /// (Action参数中自定义代码) + /// 看源代码,可能是集合类断言的基类 + /// + [Fact] + public void Collection_Test() + { + //验证集合是否包含给定元素数量,满足元素检查器提供的条件。 + + List list = new List() { "first", "second" }; + //参数Action[] elementInspectors,元素检查器,依次检查每个元素。 + //元素检查器的总数必须与集合中元素的数量完全匹配 + + //看源代码:每个Action执行时,均不抛出异常,则断言通过 + Action[] actions = new Action[2] + { + new Action (s=> {} ), + new Action (s=>{}), + }; + + Assert.Collection(list, actions); + } + + #region ISet(集合) + /// + /// 子集断言 + /// ISet是集合接口:包含不重复元素,实际集合的交、并、子集等运算 + /// 实现了ISet接口的集合:HashSet和SortedSet + /// + [Fact] + public void Subset_Test() + { + //"真实值"是"期望值"的子集 + + HashSet hset = new HashSet() { "first", "second", "third" }; + HashSet sub1 = new HashSet() { "first", "second" }; + HashSet sub2 = new HashSet() { "second", "third" }; + + //expectedSuperset 期望超集,真实值是子集 + Assert.Subset(expectedSuperset: hset, actual: sub1); + Assert.Subset(hset, sub2); + + //自己是自己子集 + Assert.Subset(hset, hset); + } + + /// + /// 超集断言 + /// + [Fact] + public void Superset_Test() + { + //"真实值"是"期望值"的超集 + + HashSet hset = new HashSet() { "first", "second", "third" }; + HashSet sub1 = new HashSet() { "first", "second" }; + HashSet sub2 = new HashSet() { "second", "third" }; + + //expectedSuperset 期望超集,真实值是子集 + Assert.Superset(sub1, hset); + Assert.Superset(sub2, hset); + + //自己是自己子的超集 + Assert.Superset(hset, hset); + } + + /// + /// 真子集断言 + /// + [Fact] + public void ProperSubset_Test() + { + //"真实值"是"期望值"的真子集 + + HashSet hset = new HashSet() { "first", "second", "third" }; + HashSet sub1 = new HashSet() { "first", "second" }; + HashSet sub2 = new HashSet() { "second", "third" }; + + //expectedSuperset 期望超集,真实值是子集 + Assert.ProperSubset(expectedSuperset: hset, actual: sub1); + Assert.ProperSubset(hset, sub2); + + //自己不是自己真子集 + //Assert.ProperSubset(hset, hset); + } + + /// + /// 真超集断言 + /// + [Fact] + public void ProperSuperset_Test() + { + //"真实值"是"期望值"的真超集 + + HashSet hset = new HashSet() { "first", "second", "third" }; + HashSet sub1 = new HashSet() { "first", "second" }; + HashSet sub2 = new HashSet() { "second", "third" }; + + //expectedSuperset 期望超集,真实值是子集 + Assert.ProperSuperset(sub1, hset); + Assert.ProperSuperset(sub2, hset); + + //自己不是自己子的真超集 + //Assert.ProperSuperset(hset, hset); + } + #endregion + + #region 事件(暂未实现) + + /// + /// 属性更改事件 + /// + [Fact] + public void PropertyChanged_Test() + { + + } + + /// + /// 属性更改事件(异步) + /// + [Fact] + public void PropertyChangedAsync_Test() + { + + } + + /// + /// 断言 特定事件被触发 + /// + [Fact] + public void Raises_Test() + { + + } + + /// + /// 断言 事件或派生事件 被触发 + /// + [Fact] + public void RaisesAny_Test() + { + + } + + /// + /// 断言 特定事件被触发(异步) + /// + [Fact] + public void RaisesAsync_Test() + { + + } + + /// + /// 断言 事件或派生事件 被触发(异步) + /// + [Fact] + public void RaisesAnyAsync_Test() + { + + } + #endregion + + #region 私有方法 + + private void ThrowArgumentNullException(string paramName) + { + ArgumentNullException ex = new ArgumentNullException(paramName, "message"); + throw ex; + } + + private ArgumentNullException ThrowAndReturnArgumentNullException(string paramName) + { + ArgumentNullException ex = new ArgumentNullException(paramName, "message"); + throw ex; + } + + private async Task ThrowAndReturnArgumentNullExceptionAsync(string paramName) + { + Func func = (para) => + { + ArgumentNullException ex = new ArgumentNullException(paramName, "message"); + throw ex; + }; + var reslut = await Task.Run(() => func(paramName)); + return reslut; + } + + private void ThrowFormatException() + { + int.Parse("ping"); + } + + private FormatException ThrowAndReturnFormatException() + { + FormatException exception = null; + try + { + int.Parse("ping"); + } + catch (FormatException ex) + { + exception = ex; + throw ex; + } + + return exception; + } + + private async Task ThrowFormatExceptionAsync() + { + await Task.Run(() => { int.Parse("ping"); }); + } + + private async Task ThrowAndReturnFormatExceptionAsync() + { + Func func = () => + { + int.Parse("ping"); + return new FormatException(); + }; + + await Task.Run(() => func()); + } + #endregion + + #region 清理 + public void Dispose() + { + + } + #endregion + } +} diff --git a/xUnitStudy.WebApi.Test/UseFixture/CollectionFixtureSetup.cs b/xUnitStudy.WebApi.Test/UseFixture/CollectionFixtureSetup.cs new file mode 100644 index 0000000..8a1155a --- /dev/null +++ b/xUnitStudy.WebApi.Test/UseFixture/CollectionFixtureSetup.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using Xunit.Sdk; + +using xUnitStudy.Model; + +namespace xUnitStudy.WebApi.Test +{ + /// + /// 设置全局共享 + /// + [CollectionDefinition("Demo Collection")] + public class CollectionFixtureSetup:ICollectionFixture + { + //这个类没有代码,而不需要创建。 + //其目的是简单地设置测试集共享数据 + //只要设置[CollectionDefinition("Demo Collection")]特性 + //并实现IClassFixture接口 + } +} diff --git a/xUnitStudy.WebApi.Test/UseFixture/UseClassFixtureTest.cs b/xUnitStudy.WebApi.Test/UseFixture/UseClassFixtureTest.cs new file mode 100644 index 0000000..e2546c5 --- /dev/null +++ b/xUnitStudy.WebApi.Test/UseFixture/UseClassFixtureTest.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using Xunit.Sdk; + +using xUnitStudy.Model; + +namespace xUnitStudy.WebApi.Test +{ + /// + /// 测试类级别的共享(类内所有测试用例,共享一个类的实例) + /// + public class UseIClassFixtureTest:IClassFixture, IDisposable + { + /* IClassFixture使用步骤: + * + * 01:创建自定义的Fixture类,添加构造函数;如需做清理,则实现IDisposable接口 + * 02:创建具体的测试类,并继承 IClassFixture(T 即是 自定义的Fixture类) + * 03:在测试类的构造函数中添加对应的注入参数来获取Fixture,这样的设计使得我们在测试类中所有的测试用例中共享一些Context数据. + * xUnit.Net 会自动发现和运用 + * + * 执行流程: + * + * 发现测试类 + * + * xUnit 发现测试类相关共享Fixture,创建共享实例并保存,与测试类关联 + * + * 用例b:创建测试类实例 --> 获取共享Fixture --> 构造函数 --> 用例1 --> Dispose()方法 --> 结束测试类实例 --> 完成 + * + * 用例a:创建测试类实例 --> 获取共享Fixture --> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + * + * 用例x:创建测试类实例 --> 获取共享Fixture --> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + * + * 用例m:创建测试类实例 --> 获取共享Fixture--> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + + * ........ + + * 本测试类中所有用例完成 + + * 清理共享实例 + * + * + * 特别注意: + * 由于测试用例执行顺序不固定,还可能并行运行 + * 所以对共享的使用,应避免出现竞态条件。 + * 否则,很容易因共享类的竞态条件,导致单元测试失败。 + * 因此:共享资源,只适合读,不适合写,使用须谨慎。 + */ + ClassFixtureDemo fixtureDemo; + + public UseIClassFixtureTest(ClassFixtureDemo fixture) + { + this.fixtureDemo = fixture; + } + + + [Fact] + public void NotNull_Test() + { + Assert.NotNull(fixtureDemo); + } + + [Fact] + public void GetPersons_Test() + { + var persons = fixtureDemo.Persons; + //因为共享,所以数量不一定是初始值3 + //Assert.Equal(3,fixtureDemo.Persons.Count); + } + + [Fact] + public void AddPerson_Test() + { + var callTimes_start = fixtureDemo.CallTimes; + + var person1 = new Person() { Id=1,FirstName="first",LastName="last"}; + var person2 = new Person() {Id=200}; + + //因为共享,在不能保证单元测试的执行先后顺充时,或者并行执行单元测试时,可能id=1的项已被删除,也可能没被删除 + //所以,result1可能失败,也可能成功 + + var result1 = fixtureDemo.AddPerson(person1); + var result2 = fixtureDemo.AddPerson(person2); + + //Assert.False(result1.result); 执行成功与否,取决于添加和删除单元测试的执行顺序。因为执行顺序不能确定,所以结果不定。 + Assert.True(result2.result); + + Assert.Equal(4, fixtureDemo.Persons.Count); + Assert.Contains(person2, fixtureDemo.Persons); + + var callTimes_end = fixtureDemo.CallTimes; + Assert.Equal(2, callTimes_end - callTimes_start); + } + + [Fact] + public void RemovePerson_Test() + { + var person1 = new Person() { Id = 1, FirstName = "first", LastName = "last" }; + var person2 = new Person() { Id = 100 }; + var result1 = fixtureDemo.RemovePerson(person1); + var result2 = fixtureDemo.RemovePerson(person2); + + Assert.True(result1.result); + Assert.False(result2.result); + + Assert.DoesNotContain(person1, fixtureDemo.Persons); + Assert.DoesNotContain(person2, fixtureDemo.Persons); + } + + public void Dispose() + { + + } + } +} diff --git a/xUnitStudy.WebApi.Test/UseFixture/UseCollectionFixtureTest.cs b/xUnitStudy.WebApi.Test/UseFixture/UseCollectionFixtureTest.cs new file mode 100644 index 0000000..ff86539 --- /dev/null +++ b/xUnitStudy.WebApi.Test/UseFixture/UseCollectionFixtureTest.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using Xunit.Sdk; + +using xUnitStudy.Model; + +namespace xUnitStudy.WebApi.Test +{ + /// + /// Collection级别的Fixture + /// 测试程序集中所有测试类可以共享 + /// + [Collection("Demo Collection")] + public class UseICollectionFixtureTest + { + /* ICollectionFixture使用步骤: + * + * 01:创建自定义的Fixture类,添加构造函数;如需做清理,则实现IDisposable接口 + * 02:创建Collection类, 添加[CollectionDefinition]特性, 并设置一个唯一名称来识别测试集合. + * 03:Collection类,实现ICollectionFixture接口,T 即是“自定义的Fixture类”. + * 04:创建具体的测试类,添加[Collection]特性,指定要使用的[CollectionDefinition]的唯一名称 + * 05:如果需要在测试类中使用共享对象,只需要把它作为一个构造函数参数。xUnit.Net 会自动发现和运用。 + * + * + * 执行流程: + * + * 运行整个测试集 + * + * xUnit 发现程序集中所有相关的共享ICollectionFixture,创建共享实例并保存,与测试集关联 + * + * 发现测试类x + * + * xUnit 发现测试类相关共享Fixture,创建共享实例并保存,与测试类关联 + * + * 用例b:创建测试类实例 --> 获取共享ICollectionFixture --> 获取共享ClassFixture --> 构造函数 --> 用例1 --> Dispose()方法 --> 结束测试类实例 --> 完成 + * + * 用例a:创建测试类实例 --> 获取共享ICollectionFixture --> 获取共享ClassFixture --> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + * + * 用例x:创建测试类实例 --> 获取共享ICollectionFixture --> 获取共享ClassFixture --> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + * + * 用例m:创建测试类实例 --> 获取共享ICollectionFixture --> 获取共享ClassFixture --> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + + * ........ + + * 本测试类中所有用例完成 + + * 清理共享实例 + * + * 其它测试类y m n a ...... + * + * .......... + * + * 所有测试类完成 + * + * ICollectionFixture共享实例,清理 + * + * 完成 + * + * 特别注意: + * 由于测试用例执行顺序不固定,还可能并行运行 + * 所以对共享对象(ICollectionFixture和ClassFixture)的使用,应避免出现竞态条件。 + * 否则,很容易因共享类的竞态条件,导致单元测试失败。 + * 因此:共享资源,只适合读,不适合写,使用须谨慎。 + */ + CollectionFixtureDemo fixtureDemo; + + public UseICollectionFixtureTest(CollectionFixtureDemo fixture) + { + fixtureDemo = fixture; + } + + [Fact] + public void Test() + { + var persons = fixtureDemo.GetPersonById(1); + + Assert.True(persons.Id == 1, "使用全局共享对象"); + } + } +} diff --git a/xUnitStudy.WebApi.Test/UseFixture/UseFixtureTest.cs b/xUnitStudy.WebApi.Test/UseFixture/UseFixtureTest.cs new file mode 100644 index 0000000..a87fb35 --- /dev/null +++ b/xUnitStudy.WebApi.Test/UseFixture/UseFixtureTest.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using Xunit.Sdk; + +using xUnitStudy.Model; + +namespace xUnitStudy.WebApi.Test +{ + /// + /// 测试用例级别的共享 + /// + public class UseFixtureTest:IDisposable + { + /* 学习 + 测试时,无论是针对一些比较耗费资源的对象亦或是为了支持Test case预设数据的能力,我们都需要有一些初始化或是清理相关的动作。 + 在每个测试用例执行前和执行后,可能需要执行相同的准备和清理工作。 + 本例方法1:放在构造函数和IDisposable接口的实现方法Dispose()中 + 执行流程: + 用例b:创建测试类实例 --> 构造函数 --> 用例1 --> Dispose()方法 --> 结束测试类实例 --> 完成 + + 用例a:创建测试类实例 --> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + + 用例x:创建测试类实例 --> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + + 用例m:创建测试类实例 --> 构造函数 --> 用例2 --> Dispose()方法 --> 结束测试类实例 --> 完成 + + ........ + + 所有用例完成 + + 注意:用例执行的先后顺序由xUnit框架指定,且先后顺序不固定。尤其是并行执行时,执行顺序和返回顺序更不确定。 + 所以,各单元测试是完全独立的,相互之间不能存在任何依赖,特别是执行顺序、执行结果、共享数据。 + */ + + object sharedData; + public UseFixtureTest() + { + //测试用例执行前做准备 + sharedData = "sharedate"; + } + + [Fact] + public void Case2_Test() + { + Assert.Equal("sharedate", sharedData.ToString()); + } + + [Fact] + public void Case1_Test() + { + Assert.Equal("sharedate", sharedData.ToString()); + } + + public void Dispose() + { + //测试用例执行后做清理 + sharedData = null; + } + } +} diff --git a/xUnitStudy.WebApi.Test/UseFixture/UseFullFixtures.cs b/xUnitStudy.WebApi.Test/UseFixture/UseFullFixtures.cs new file mode 100644 index 0000000..40f4549 --- /dev/null +++ b/xUnitStudy.WebApi.Test/UseFixture/UseFullFixtures.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using Xunit.Sdk; + +using xUnitStudy.Model; + +namespace xUnitStudy.WebApi.Test.UseFixture +{ + /// + /// 同时使用多种共享 + /// + [Collection("Demo Collection")] + public class UseFullFixtures : IDisposable, IClassFixture + { + CollectionFixtureDemo collectionFixture; + ClassFixtureDemo classFixture; + + public UseFullFixtures(CollectionFixtureDemo collectionDemo, ClassFixtureDemo fixtureDemo) + { + classFixture = fixtureDemo; + collectionFixture = collectionDemo; + } + + [Fact] + public void Test() + { + Assert.NotNull(classFixture); + + var person = collectionFixture.GetPersonById(2); + Assert.Equal(2, person.Id); + } + + public void Dispose() + { + //每个测试用例的清理 + } + } +} diff --git a/xUnitStudy.WebApi.Test/UseOutputTest.cs b/xUnitStudy.WebApi.Test/UseOutputTest.cs new file mode 100644 index 0000000..430c11e --- /dev/null +++ b/xUnitStudy.WebApi.Test/UseOutputTest.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace xUnitStudy.WebApi.Test +{ + public class UseOutputTest + { + private readonly ITestOutputHelper output; + public UseOutputTest(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public void Test() + { + Assert.True(true, "输出测试"); + + output.WriteLine("我是输出测试信息哦。"); + } + } +} diff --git a/xUnitStudy.WebApi.Test/UseXUnitTest.cs b/xUnitStudy.WebApi.Test/UseXUnitTest.cs index 94c26f5..6a15c82 100644 --- a/xUnitStudy.WebApi.Test/UseXUnitTest.cs +++ b/xUnitStudy.WebApi.Test/UseXUnitTest.cs @@ -5,15 +5,113 @@ using System.Text; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; +using Xunit.Extensions; +using Xunit.Sdk; namespace xUnitStudy.WebApi.Test { - public class UseXUnitTest + /// + /// 使用 xUnit + /// 测试类不需要加任何特性 + /// NUnit需要加TestFixture特性,MSTest需要加TestClass特性 + /// + [Trait(name:"测试类分组",value:"包括类内所有测试方法")] + public class UseXUnitTest:IDisposable { + /// + /// 每次测试前的准备 + /// 构造函数 替代 SetUp或TestInitialize,进行初始化 + /// + public UseXUnitTest() + { + + } + + /// + /// 每次测试后的清理 + /// 实现IDisposable接口,替代TearDown或TestCleanup 进行清理释放操作 + /// + public void Dispose() + { + + } + + /// + /// Fact特性,标识测试方法 + /// + [Fact] + public void Use_Fact_Test() + { + //永久通过测试断言的写法 + Assert.True(true,"Fact特性,表示测试方法"); + } + + [Fact(DisplayName = "定制测试方法名称")] + public void Use_Fact_DisplayName_Test() + { + Assert.True(true,"使用Fact特性的DisplayName属性,表示:定制的测试方法名称"); + } + + [Fact(Skip = "暂时忽略测试")] + public void Use_Fact_Skip_Test() + { + Assert.True(true,"使用Fact特性的Skip属性,表示:暂时忽略测试"); + } + + /// + /// 使用Fact特性的Timeout参数 + /// 设置:测试方法超时时间(豪秒) + /// + [Fact(Timeout =1000)] + public void Use_Fact_Timeout_Test() + { + Assert.True(true,"使用Fact特性的Timeout属性,设置:测试方法超时时间(豪秒)"); + } + + /// + /// 分组测试,可以按分组显示测试结果 + /// Trait可用于:程序集(AssemblyInfo.cs文件中)、测试类和测试方法三个层次 + /// 一个测试方法同时属于多外组 + /// + [Fact] + [Trait(name:"测试方法分组",value:"红队")] + [Trait(name:"测试方法分组",value:"蓝队")] + public void Use_Trait_Test() + { + Assert.True(true,"使用Trait特性,设置:测试分组,测试结果可以按组显示"); + } + + /// + /// 测试方法分组:红队 + /// [Fact] - public void Test() + [Trait(name: "测试方法分组", value: "红队")] + public void Use_Trait_Red_Test() { - Assert.Equal(1, 1); + Assert.True(true, "使用Trait特性,设置:测试分组,测试结果可以按组显示"); + } + + /// + /// 测试方法分组:蓝队 + /// + [Fact] + [Trait(name: "测试方法分组", value: "蓝队")] + public void Use_Trait_Blue_Test() + { + Assert.True(true, "使用Trait特性,设置:测试分组,测试结果可以按组显示"); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + public void Use_Theory_Test(int number) + { + var userPara = number; + + Assert.True(true,"使用Theory特性和InlineData特性,传递不同参数,多次执行测试"); } } } diff --git a/xUnitStudy.WebApi.Test/app.config b/xUnitStudy.WebApi.Test/app.config index 58c97be..88b58bf 100644 --- a/xUnitStudy.WebApi.Test/app.config +++ b/xUnitStudy.WebApi.Test/app.config @@ -1,5 +1,8 @@  + + + @@ -22,6 +25,10 @@ + + + + \ No newline at end of file diff --git a/xUnitStudy.WebApi.Test/packages.config b/xUnitStudy.WebApi.Test/packages.config index 8170e97..cd86010 100644 --- a/xUnitStudy.WebApi.Test/packages.config +++ b/xUnitStudy.WebApi.Test/packages.config @@ -1,7 +1,11 @@  + + + + diff --git a/xUnitStudy.WebApi.Test/xUnitStudy.WebApi.Test.csproj b/xUnitStudy.WebApi.Test/xUnitStudy.WebApi.Test.csproj index 85a8fb5..bc467eb 100644 --- a/xUnitStudy.WebApi.Test/xUnitStudy.WebApi.Test.csproj +++ b/xUnitStudy.WebApi.Test/xUnitStudy.WebApi.Test.csproj @@ -34,6 +34,12 @@ 4 + + ..\packages\Autofac.4.0.1\lib\net45\Autofac.dll + + + ..\packages\Autofac.Extras.Moq.4.2.0\lib\net45\Autofac.Extras.Moq.dll + ..\packages\Castle.Core.4.3.1\lib\net45\Castle.Core.dll @@ -41,6 +47,12 @@ ..\packages\Moq.4.9.0\lib\net45\Moq.dll + + ..\packages\System.ComponentModel.Primitives.4.1.0\lib\net45\System.ComponentModel.Primitives.dll + + + ..\packages\System.ComponentModel.TypeConverter.4.1.0\lib\net45\System.ComponentModel.TypeConverter.dll + @@ -49,6 +61,7 @@ ..\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll + @@ -69,8 +82,18 @@ + + + + + + + + + + @@ -107,7 +130,9 @@ xUnitStudy.WebApi - + + +