第一部分: http://www.cnblogs.com/cgzl/p/8283610.html

下面有一点点内容是重叠的....

String Assert

测试string是否相等

        [Fact]
public void CalculateFullName()
{
var p = new Patient
{
FirstName = "Nick",
LastName = "Carter"
};
Assert.Equal("Nick Carter", p.FullName);
}

然后你需要Build一下,这样VS Test Explorer才能发现新的test。

运行测试,结果Pass:

同样改一下Patient类(别忘了Build一下),让结果失败:

从失败信息可以看到期待值和实际值。

StartsWith, EndsWith

        [Fact]
public void CalculateFullNameStartsWithFirstName()
{
var p = new Patient
{
FirstName = "Nick",
LastName = "Carter"
};
Assert.StartsWith("Nick", p.FullName);
} [Fact]
public void CalculateFullNameEndsWithFirstName()
{
var p = new Patient
{
FirstName = "Nick",
LastName = "Carter"
};
Assert.EndsWith("Carter", p.FullName);e);
}

Build,然后Run Test,结果Pass:

忽略大小写 ignoreCase

string默认的Assert是区分大小写的,这样就会失败:

可以为这些方法添加一个参数ignoreCase设置为true,就会忽略大小写:

包含子字符串 Contains

        [Fact]
public void CalculateFullNameSubstring()
{
var p = new Patient
{
FirstName = "Nick",
LastName = "Carter"
};
Assert.Contains("ck Ca", p.FullName);
}

Build,测试结果Pass。

正则表达式,Matches

测试一下First name和Last name的首字母是不是大写的:

        [Fact]
public void CalculcateFullNameWithTitleCase()
{
var p = new Patient
{
FirstName = "Nick",
LastName = "Carter"
};
Assert.Matches("[A-Z]{1}{a-z}+ [A-Z]{1}[a-z]+", p.FullName);
}

Build,测试通过。

数值 Assert

首先为Patient类添加一个property: BloodSugar。

    public class Patient
{
public Patient()
{
IsNew = true;
_bloodSugar = 5.0f;
} private float _bloodSugar;
public float BloodSugar
{
get { return _bloodSugar; }
set { _bloodSugar = value; }
}
...

Equal:

        [Fact]
public void BloodSugarStartWithDefaultValue()
{
var p = new Patient();
Assert.Equal(5.0, p.BloodSugar);
}

Build,测试通过。

范围, InRange:

首先为Patient类添加一个方法,病人吃饭之后血糖升高:

        public void HaveDinner()
{
var random = new Random();
_bloodSugar += (float)random.Next(1, 1000) / 100; // 应该是1000
}

添加test:

        [Fact]
public void BloodSugarIncreaseAfterDinner()
{
var p = new Patient();
p.HaveDinner();
// Assert.InRange<float>(p.BloodSugar, 5, 6);
Assert.InRange(p.BloodSugar, 5, 6);
}

Build,Run Test,结果Fail:

可以看到期待的Range和实际的值,这样很好。如果你使用Assert.True(xx >= 5 && xx <= 6)的话,错误信息只能显示True或者False。

因为HaveDinner方法里,表达式的分母应该是1000,修改后,Build,Run,测试Pass。

浮点型数值的Assert

在被测项目添加这两个类:

namespace Hospital
{
public abstract class Worker
{
public string Name { get; set; } public abstract double TotalReward { get; }
public abstract double Hours { get; }
public double Salary => TotalReward / Hours;
} public class Plumber : Worker
{
public override double TotalReward => ;
public override double Hours => ;
}
}

然后针对Plumber建立一个测试类 PlumberShould.cs, 并建立第一个test:

namespace Hospital.Tests
{
public class PlumberShould
{
[Fact]
public void HaveCorrectSalary()
{
var plumber = new Plumber();
Assert.Equal(66.666, plumber.Salary);
}
}
}

Build项目, 然后再Test Explorer里面选择按Class分类显示Tests:

Run Selected Test, 结果会失败:

这是一个精度的问题.

在Assert.Equal方法, 可以添加一个precision参数, 设置精度为3:

        [Fact]
public void HaveCorrectSalary()
{
var plumber = new Plumber();
Assert.Equal(66.666, plumber.Salary, precision: 3);
}

Build, Run Test:

因为有四舍五入的问题, 所以test仍然fail了.

所以还需要改一下:

        [Fact]
public void HaveCorrectSalary()
{
var plumber = new Plumber();
Assert.Equal(66.66, plumber.Salary, precision: );
}

这次会pass的:

Assert Null值

        [Fact]
public void NotHaveNameByDefault()
{
var plumber = new Plumber();
Assert.Null(plumber.Name);
} [Fact]
public void HaveNameValue()
{
var plumber = new Plumber
{
Name = "Brian"
};
Assert.NotNull(plumber.Name);
}

有两个方法, Assert.Null 和 Assert.NotNull, 直接传入期待即可.

测试会Pass的.

集合 Collection Assert

修改一下被测试类, 添加一个集合属性, 并赋值:

namespace Hospital
{
public abstract class Worker
{
public string Name { get; set; } public abstract double TotalReward { get; }
public abstract double Hours { get; }
public double Salary => TotalReward / Hours; public List<string> Tools { get; set; }
} public class Plumber : Worker
{
public Plumber()
{
Tools = new List<string>()
{
"螺丝刀",
"扳子",
"钳子"
};
} public override double TotalReward => ;
public override double Hours => ;
}
}

测试是否包含某个元素, Assert.Contains():

        [Fact]
public void HaveScrewdriver()
{
var plumber = new Plumber();
Assert.Contains("螺丝刀", plumber.Tools);
}

Build, Run Test, 结果Pass.

修改一下名字, 让其Fail:

这个失败信息还是很详细的.

相应的还有一个Assert.DoesNotContain()方法, 测试集合是否不包含某个元素.

        [Fact]
public void NotHaveKeyboard()
{
var plumber = new Plumber();
Assert.DoesNotContain("键盘", plumber.Tools);
}

这个test也会pass.

Predicate:

测试一下集合中是否包含符合某个条件的元素:

        [Fact]
public void HaveAtLeastOneScrewdriver()
{
var plumber = new Plumber();
Assert.Contains(plumber.Tools, t => t.Contains("螺丝刀"));
}

使用的是Assert.Contains的一个overload方法, 它的第一个参数是集合, 第二个参数是Predicate.

Build, Run Test, 会Pass的.

比较集合相等:

添加Test:

        [Fact]
public void HaveAllTools()
{
var plumber = new Plumber();
var expectedTools = new []
{
"螺丝刀",
"扳子",
"钳子"
};
Assert.Equal(expectedTools, plumber.Tools);
}

注意, Plumber的tools类型是List, 这里的expectedTools类型是array.

这个test 仍然会Pass.

如果修改一个元素, 那么测试会Fail, 信息如下:

Assert针对集合的每个元素:

如果想对集合的每个元素进行Assert, 当然可以通过循环来Assert了, 但是更好的写法是调用Assert.All()方法:

        [Fact]
public void HaveNoEmptyDefaultTools()
{
var plumber = new Plumber();
Assert.All(plumber.Tools, t => Assert.False(string.IsNullOrEmpty(t)));
}

这个测试会Pass.

如果在被测试类的Tools属性添加一个空字符串, 那么失败信息会是:

这里写到, 4个元素里面有1个没有pass.

针对Object类型的Assert

首先再添加一个Programmer类:

    public class Programmer : Worker
{
public override double TotalReward => ;
public override double Hours => 3.5;
}

然后建立一个WorkerFactory:

namespace Hospital
{
public class WorkerFactory
{
public Worker Create(string name, bool isProgrammer = false)
{
if (isProgrammer)
{
return new Programmer { Name = name };
}
return new Plumber { Name = name };
}
}
}

判断是否是某个类型 Assert.IsType<Type>(xx):
建立一个测试类 WorkerShould.cs和一个test:

namespace Hospital.Tests
{
public class WorkerShould
{
[Fact]
public void CreatePlumberByDefault()
{
var factory = new WorkerFactory();
Worker worker = factory.Create("Nick");
Assert.IsType<Plumber>(worker);
}
}
}

Build, Run Test: 结果Pass.

相应的, 还有一个Assert.IsNotType<Type>(xx)方法.

利用Assert.IsType<Type>(xx)的返回值, 它会返回Type(xx的)的这个实例, 添加个一test:

        [Fact]
public void CreateProgrammerAndCastReturnedType()
{
var factory = new WorkerFactory();
Worker worker = factory.Create("Nick", isProgrammer: true);
Programmer programmer = Assert.IsType<Programmer>(worker);
Assert.Equal("Nick", programmer.Name);
}

Build, Run Tests: 结果Pass.

Assert针对父类:

写这样一个test, 创建的是一个promgrammer, Assert的类型是它的父类Worker:

        [Fact]
public void CreateProgrammer_AssertAssignableTypes()
{
var factory = new WorkerFactory();
Worker worker = factory.Create("Nick", isProgrammer: true);
Assert.IsType<Worker>(worker);
}

这个会Fail:

这时就应该使用这个方法, Assert.IsAssignableFrom<祖先类>(xx):

        [Fact]
public void CreateProgrammer_AssertAssignableTypes()
{
var factory = new WorkerFactory();
Worker worker = factory.Create("Nick", isProgrammer: true);
Assert.IsAssignableFrom<Worker>(worker);
}

Build, Run Tests: Pass.

Assert针对对象的实例

判断两个引用是否指向不同的实例 Assert.NotSame(a, b):

        [Fact]
public void CreateSeperateInstances()
{
var factory = new WorkerFactory();
var p1 = factory.Create("Nick");
var p2 = factory.Create("Nick");
Assert.NotSame(p1, p2);
}

由工厂创建的两个对象是不同的实例, 所以这个test会Pass.

相应的还有个Assert.Same(a, b) 方法.

Assert 异常

为WorkFactory先添加一个异常处理:

namespace Hospital
{
public class WorkerFactory
{
public Worker Create(string name, bool isProgrammer = false)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (isProgrammer)
{
return new Programmer { Name = name };
}
return new Plumber { Name = name };
}
}
}

如果在test执行代码时抛出异常的话, 那么test会直接fail掉.

所以应该使用Assert.Throws<ArgumentNullException>(...)方法来Assert是否抛出了特定类型的异常.

添加一个test:

        [Fact]
public void NotAllowNullName()
{
var factory = new WorkerFactory();
// var p = factory.Create(null); // 这个会失败
Assert.Throws<ArgumentNullException>(() => factory.Create(null));
}

注意不要直接运行会抛出异常的代码. 应该在Assert.Throws<ET>()的方法里添加lambda表达式来调用方法.

这样的话就会pass.

如果被测试代码没有抛出异常的话, 那么test会fail的. 把抛异常代码注释掉之后再Run:

更具体的, 还可以指定参数的名称:

        [Fact]
public void NotAllowNullName()
{
var factory = new WorkerFactory();
// Assert.Throws<ArgumentNullException>(() => factory.Create(null));
Assert.Throws<ArgumentNullException>("name", () => factory.Create(null));
}

这里就是说异常里应该有一个叫name的参数.

Run: Pass.

如果把"name"改成"isProgrammer", 那么这个test会fail:

利用Assert.Throws<ET>()的返回结果, 其返回结果就是这个抛出的异常实例.

        [Fact]
public void NotAllowNullNameAndUseReturnedException()
{
var factory = new WorkerFactory();
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => factory.Create(null));
Assert.Equal("name"
, ex.ParamName);
}

Assert Events 是否发生(Raised)

回到之前的Patient类, 添加如下代码:

        public void Sleep()
{
OnPatientSlept();
} public event EventHandler<EventArgs> PatientSlept; protected virtual void OnPatientSlept()
{
PatientSlept?.Invoke(this, EventArgs.Empty);
}

然后回到PatientShould.cs添加test:

        [Fact]
public void RaiseSleptEvent()
{
var p = new Patient();
Assert.Raises<EventArgs>(
handler => p.PatientSlept += handler,
handler => p.PatientSlept -= handler,
() => p.Sleep());
}

Assert.Raises<T>()第一个参数是附加handler的Action, 第二个参数是分离handler的Action, 第三个Action是触发event的代码.

Build, Run Test: Pass.

如果注释掉Patient类里Sleep()方法内部那行代码, 那么test会fail:

针对INotifyPropertyChanged的特殊Assert:

修改Patient代码:

namespace Hospital
{
public class Patient: INotifyPropertyChanged
{
public Patient()
{
IsNew = true;
_bloodSugar = 5.0f;
} public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public int HeartBeatRate { get; set; }
public bool IsNew { get; set; } private float _bloodSugar;
public float BloodSugar
{
get => _bloodSugar;
set => _bloodSugar = value;
} public void HaveDinner()
{
var random = new Random();
_bloodSugar += (float)random.Next(, ) / ;
OnPropertyChanged(nameof(BloodSugar));
} public void IncreaseHeartBeatRate()
{
HeartBeatRate = CalculateHeartBeatRate() + ;
} private int CalculateHeartBeatRate()
{
var random = new Random();
return random.Next(, );
} public void Sleep()
{
OnPatientSlept();
} public event EventHandler<EventArgs> PatientSlept; protected virtual void OnPatientSlept()
{
PatientSlept?.Invoke(this, EventArgs.Empty);
} public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

添加一个Test:

        [Fact]
public void RaisePropertyChangedEvent()
{
var p = new Patient();
Assert.PropertyChanged(p, "BloodSugar", () => p.HaveDinner());
}

针对INotifyPropertyChanged, 可以使用Assert.PropertyChanged(..) 这个专用的方法来断定PropertyChanged的Event是否被触发了.

Build, Run Tests: Pass.

到目前为止, 介绍的都是入门级的内容.

接下来要介绍的是稍微进阶一点的内容了.

出处:http://www.cnblogs.com/cgzl/p/8287588.html

使用xUnit为.net core程序进行单元测试(2)的更多相关文章

  1. 使用xUnit为.net core程序进行单元测试(上)

    一. 导读 为什么要编写自动化测试程序(Automated Tests)? 可以频繁的进行测试 可以在任何时间进行测试,也可以按计划定时进行,例如:可以在半夜进行自动测试. 肯定比人工测试要快. 可以 ...

  2. 使用xUnit为.net core程序进行单元测试(1)

    导读 为什么要编写自动化测试程序(Automated Tests)? 可以频繁的进行测试 可以在任何时间进行测试,也可以按计划定时进行,例如:可以在半夜进行自动测试. 肯定比人工测试要快. 可以更快速 ...

  3. 使用xUnit为.net core程序进行单元测试(中)

    第一部分: http://www.cnblogs.com/cgzl/p/8283610.html 下面有一点点内容是重叠的.... String Assert 测试string是否相等: [Fact] ...

  4. 使用xUnit为.net core程序进行单元测试(3)

    第1部分: http://www.cnblogs.com/cgzl/p/8283610.html 第2部分: http://www.cnblogs.com/cgzl/p/8287588.html 请使 ...

  5. 使用xUnit为.net core程序进行单元测试(4)

    第1部分: http://www.cnblogs.com/cgzl/p/8283610.html 第2部分: http://www.cnblogs.com/cgzl/p/8287588.html 第3 ...

  6. 使用xUnit为.net core程序进行单元测试 -- Assert

    第一部分: http://www.cnblogs.com/cgzl/p/8283610.html Assert Assert做什么?Assert基于代码的返回值.对象的最终状态.事件是否发生等情况来评 ...

  7. 使用xUnit为.net core程序进行单元测试

      第1部分: http://www.cnblogs.com/cgzl/p/8283610.html 第2部分: http://www.cnblogs.com/cgzl/p/8287588.html ...

  8. 好代码是管出来的——.Net Core中的单元测试与代码覆盖率

    测试对于软件来说,是保证其质量的一个重要过程,而测试又分为很多种,单元测试.集成测试.系统测试.压力测试等等,不同的测试的测试粒度和测试目标也不同,如单元测试关注每一行代码,集成测试关注的是多个模块是 ...

  9. .NET Core: 在.NET Core中进行单元测试

    单元测试能够帮助开发人员确保所开发的模块.类以及类中的方法等的正确性,在项目开发过程中,及时进行单元测试能够避免不必要的BUG以及提高测试效率. 在本文中,我们会分别来学习如何使用MSTest.xUn ...

随机推荐

  1. HashMap 遍历的两种方式及性能比较

    HashMap 是Java开发中经常使用的数据结构.相信HashMap 的基本用法你已经很熟悉了.那么我们该如何遍历HashMap 呢?哪种遍历方式的性能更好呢?本篇文章来为你解决这个疑惑. 一.Ha ...

  2. [UI基础][QQ登陆界面]

    [目标] 1.QQ号码文本框要有“请输入QQ号码”的提示(用户输入时会自动消失) 2.QQ密码文本框要有“请输入QQ密码”的提示(用户输入文字会自动消失) 3.QQ号码文本框只能输入数字 4.QQ密码 ...

  3. Ubuntu16.04下配置pip国内镜像源加速安装【转】

    本文转载自:https://blog.csdn.net/yucicheung/article/details/79095742 问题描述 基于国内网速的问题,我们直接pip安装包通常速度非常慢,而且经 ...

  4. 如何解决Nginx php 50x 错误

    SEO反馈百度爬虫经常504,一般情况下是由nginx默认的fastcgi进程响应慢引起的,但也有其他情况,这里我总结了一些解决办法供大家参考.   方法/步骤 一般50x状态码问题分析: Nginx ...

  5. Python学习札记(二十二) 函数式编程3 filter & SyntaxError: unexpected EOF while parsing

    参考: filter Problem SyntaxError: unexpected EOF while parsing 遇到该语法错误,一般是由于 括号不匹配 问题. Note 1.filter 用 ...

  6. BZOJ 4013 【HNOI2015】 实验比较

    题目链接:实验比较 如果我们把相等关系全部缩起来的话,这道题给出的小于关系如果有环,那么就是不合法的,否则就构成了一片森林. 定义等于号连起来的所有变量看做一个块. 然后我们就可以令\(f_{i,j} ...

  7. 上海仪电Azure Stack技术深入浅出系列1:谈Azure Stack在私有云/混合云生态中的定位

    2.2 Azure Stack Azure Stack到2017年7月才提供GA版本,但目前还是可以通过技术预览版了解该技术.Azure Stack本质上是核心Azure服务的一个私有实例. Micr ...

  8. PHPUnit简介及使用(thinkphp5的单元测试安装及使用)

    PHPUnit简介及使用(thinkphp5的单元测试安装及使用) 一.总结 一句话总结:直接google这个phpunit(how to use phpunit),然后去官网看使用样例和手册,那些英 ...

  9. 【C#笔札】Tryparse的用法

    这是参考读物的上得一个例子.自己仿照做的作业 private void button1_Click(object sender, EventArgs e) { int P_int_Number,i; ...

  10. Oracle数据库空值操作

    空值操作: null表示空的意思. 一.情况: 1:表中的任何字段默认情况下都可以为null值. 2:not null表示非空,是一种约束 设置为非空约束的字段,必须有有效值,不能为空. 3:插入数据 ...