xUnit Summary - Study Notes

xUnit.net is for the .NET  Framework is free, open source, community-centered unit testing tools.

The advantages of automated testing #

  • Can be tested frequently
  • Can be tested at any time, can also be timed according to plan, for example: automated testing can be carried out in the middle of the night
  • Speed ​​is faster than manual testing
  • You can more quickly find errors
  • Basically it is very reliable
  • Test code and production code closely
  • Making development teams more happiness

Automated Testing Category #

Test unit testing a class or method, having a higher depth, the application coverage is very functional.
Integration testing have better breadth, you can test web resources, database resources.
Subcutaneous test test in the web for a node in the controller.
UI testing is the application interface functional test.
In fact commonly used is a unit testing and integration testing.

Test test private methods or behavioral #

General Public method is tested against the class, which is to conduct the test, if it is necessary to change the modifier private methods to test

xUnit.Net Features: #

  • When supporting multiple platforms / Run
  • Parallel Test
  • Data-driven testing
  • Scalable

xUnit supported platforms: #

.Net Framework
.Net Core
.Net Standard
UWP
Xamarin

Official website:
https://xunit.net

test tools:

VS自带的测试浏览器(右键测试或者ctrl+r,t)
resharper,
cmd命令行(.net cli): 
	dotnet test
		dotnet test --help

Simple example:

1, created in VS a solution, creating a .net core class library, add a Calculator class:

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

2, in the same solution, create a xUnit test project: DemoTest, for project testing, usually named after the project name + Test test project. Creating a class: CalculatorTests:

public class CalculatorTests
{
    
    [Fact]
	public void ShouldAddEquals5() //注意命名规范
	{
	    //Arrange
	    var sut = new Calculator(); //sut-system under test,通用命名
	    //Act
	    var result = sut.Add(3, 2);
	    //Assert
	    Assert.Equal(5, result);

	}
}

3、

  1. Run the test (either method):
    1. Vs testing comes through Explorer, find the test project, select Run;

Testing behavior or test private methods:

public void IncreaseHeartBeatRate()
{
    HeartBeatRate = CalculateHeartBeatRate() + 2
}

private int CalculateHeartBeatRate()
{
    var random = new Random();
    return random.Next(1,100);
}

    1. ShouldAddEquals5 by the method, the test run or the right to select the shortcut key (ctrl + r, t)
    1. By cmd, in a test run dotnet test project directory

    1. ReSharper (not installed, too time-consuming memory)

Three stages of testing: AAA #

Arrange: Here to do some pre-set. For example, create an object instance, the data input and the like.
Act: Here execute production code and returns the result. Such as calling methods or setting properties.
Assert: Here test results, pass or fail the test will produce two results.


Assert

Assert case based on the return value of the code, the final state of the object, such as whether the event occurred to assess the results of the test
results may be Assert Pass or Fail
if all asserts pass, then passed the entire test.
If any assert fails, then the results would have failed.

There should be a test of how many asserts

  1. A simple approach is that each test there is only one way assert.
  2. And there is a suggestion that each test which can have multiple asserts, as long as these asserts are the same behavior for.
    xUnit provides the following types of Assert:

Assert Methods #

Demo:
first build a the .NET  Core class library project, and then build a xunit test project (see last comprehensive example)

Assert.True,Assert.False#

 

Copy

[Fact] [Trait("Category","New")] public void BeNewWhenCreated() { _output.WriteLine("第一个测试"); // Arrange var patient = new Patient(); // Act var result = patient.IsNew; // Assert Assert.True(result); }

String test results: Assert.Equal #

 

Copy

[Fact] public void HaveCorrectFullName() { //var patient = new Patient(); _patient.FirstName = "Nick"; _patient.LastName = "Carter"; var fullName = _patient.FullName; Assert.Equal("Nick Carter", fullName); //相等 Assert.StartsWith("Nick", fullName);//以开头 Assert.EndsWith("Carter", fullName);//以结尾 Assert.Contains("Carter", fullName);//包含 Assert.Contains("Car", fullName); Assert.NotEqual("CAR", fullName);//不相等 Assert.Matches(@"^[A-Z][a-z]*\s[A-Z][a-z]*", fullName);//正则表达式 }

Digital test results #

 

Copy

[Fact] [Trait("Category", "New")] public void HaveDefaultBloodSugarWhenCreated() { var p = new Patient(); var bloodSugar = p.BloodSugar; Assert.Equal(4.9f, bloodSugar,5); //判断是否相等 Assert.InRange(bloodSugar, 3.9, 6.1);//判断是否在某一范围内 }

Judgment null, not null #

 

Copy

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

Set of test #

 

Copy

[Fact] public void HaveHadAColdBefore() { //Arrange var _patient = new Patient(); //Act var diseases = new List<string> { "感冒", "发烧", "水痘", "腹泻" }; _patient.History.Add("发烧"); _patient.History.Add("感冒"); _patient.History.Add("水痘"); _patient.History.Add("腹泻"); //Assert //判断集合是否含有或者不含有某个元素 Assert.Contains("感冒",_patient.History); Assert.DoesNotContain("心脏病", _patient.History); //判断p.History至少有一个元素,该元素以水开头 Assert.Contains(_patient.History, x => x.StartsWith("水")); //判断集合的长度 Assert.All(_patient.History, x => Assert.True(x.Length >= 2)); //判断集合是否相等,这里测试通过,说明是比较集合元素的值,而不是比较引用 Assert.Equal(diseases, _patient.History); }

Test object #

 

Copy

/// <summary> /// 测试Object /// </summary> [Fact] public void BeAPerson() { var p = new Patient(); var p2 = new Patient(); Assert.IsNotType<Person>(p); //测试对象是否相等,注意这里为false Assert.IsType<Patient>(p); Assert.IsAssignableFrom<Person>(p);//判断对象是否继承自Person,true //判断是否为同一个实例 Assert.NotSame(p, p2); //Assert.Same(p, p2); }

Determine whether the abnormal #

 

Copy

/// <summary> /// 判断是否发生异常 /// </summary> [Fact] public void ThrowException() //注意不能使用ctrl+R,T快捷键,因为会中断测试,抛出异常 { var p = new Patient(); //判断是否返回指定类型的异常 var ex = Assert.Throws<InvalidOperationException>(()=> { p.NotAllowed(); }); //判断异常信息是否相等 Assert.Equal("not able to create", ex.Message); }

Determine whether the trigger event #

 

Copy

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

Determine whether the property change trigger event #

 

Copy

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

Grouping, ignore, log, shared context

Test packet #

Use trait characteristics, test group: [Trait ( "Name", "Value")] can be applied to the methods and class Class level
the same packet using the same characteristics.

 

Copy

[Fact] [Trait("Category","New")] public void BeNewWhenCreated() { _output.WriteLine("第一个测试"); // Arrange //var patient = new Patient(); // Act var result = _patient.IsNew; // Assert Assert.True(result); //Assert.False(result); }

Test packet search: You can press arranged in groups, search, run the tests in the test Explorer

In dotnet cli packet test:

 

Copy

dotnew test --filter “Category=New” //运行单个分类测试 dotnew test --filter “Category=New|Category=Add”//运行多个分类测试 dotnet test --filter Category --logger:trx //输出测试日志

Ignoring tests #

Use characteristics: [Fact (Skip = "do not run this test")], you can ignore test ignore test yellow warning icon

Custom test output content #

Use ITestOutputHelper can customize the content in the test output
dotnet --filter Category --logger test: trx trx will output test end of the log file

 

Copy

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

Reduce duplication of code #

  1. Reduce new object in the new constructor may be used in the method.
  2. IDispose test class implements an interface, complete test release of resources, attention will call the Dispose method after the end of each test.

Shared context #

The same test class #

When you execute a method, it takes a long event, while new in the constructor, each test run is going to be new objects or execution method, which is causing the test very slow. Solution:

  1. Create a class:
 

Copy

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

  1. Test class implements IClassFixture <LongTimeFixture> interface and acquisition method in the constructor
 

Copy

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

Test different class #

1. On the succession of one, first create a TaskCollection class that implements ICollectionFixture <LongTimeFixture> Interface, be careful not to have side effects, otherwise it will affect the results

 

Copy

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

  1. Use, plus [Collection ( "Lone Time Task Collection")]
 

Copy

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


data sharing

1. [Theory], the test method may have to write configuration parameters used to pass data InlineData #

 

Copy

[Theory] [InlineData(1,2,3)] [InlineData(2,2,4)] [InlineData(3,3,6)] public void ShouldAddEquals(int operand1,int operand2,int expected) { //Arrange var sut = new Calculator(); //sut-system under test //Act var result = sut.Add(operand1, operand2); //Assert Assert.Equal(expected, result); }

2. [MemberData] characteristics, can be used in a plurality of test #

  1. First add CalculatorTestData categories:
 

Copy

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

  1. Use MemberData
 

Copy

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

3. External Data #

  1. Create a class, prepare the data, where the data is read csv file
 

Copy

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

  1. csv data
 

Copy

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

  1. use
 

Copy

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

4. Use custom properties, inherited from DataAttribute shall #

  1. Custom Properties
 

Copy

using System.Collections.Generic; using System.Reflection; using Xunit.Sdk; namespace DemoTest.Data { public class CalculatorDataAttribute : DataAttribute { public override IEnumerable<object[]> GetData(MethodInfo testMethod) { yield return new object[] { 0, 100, 100 }; yield return new object[] { 1, 99, 100 }; yield return new object[] { 2, 98, 100 }; yield return new object[] { 3, 97, 100 }; } } }

  1. use
 

Copy

/// <summary> /// 数据共享-自定义特性继承自DataAttribute /// </summary> /// <param name="operand1"></param> /// <param name="operand2"></param> /// <param name="expected"></param> [Theory] [CalculatorDataAttribute] public void ShouldAddEquals4(int operand1, int operand2, int expected) { //Arrange var sut = new Calculator(); //sut-system under test //Act var result = sut.Add(operand1, operand2); //Assert Assert.Equal(expected, result); }

Source: https://gitee.com/Alexander360/LearnXUnit

 

 

Published 91 original articles · won praise 47 · views 90000 +

Guess you like

Origin blog.csdn.net/qq_30007885/article/details/105050194