在编写单元测试用例来测试抛出异常时,尝试使用xUnit在下面两种方法中使用
以下是代码
public class Me : Entity, IAggregateRoot
{
public string Name {get; }
private List<Friend> friends;
public IReadonlyCollection<Friend> Friends => friends;
public Me(string name)
{
Name = name;
friends = new List<Friend>();
}
public void AddFriend(Friend friend)
{
if(friend.Type != "UnKnown")
friend.Add(friend);
else
throw new Exception("Cannot add a unknown friend.");
}
}
public class Friend
{
public string Name {get; }
public string Type {get; }
public Friend(string name, string type)
{
Name = name;
Type = type;
}
}
using System;
using MediatR;
using System.Collections.Generic;
public abstract class Entity
{
int? _requestedHashCode;
int _Id;
public virtual int Id
{
get
{
return _Id;
}
protected set
{
_Id = value;
}
}
private List<INotification> _domainEvents;
public IReadOnlyCollection<INotification> DomainEvents => _ domainEvents?.AsReadOnly();
public void AddDomainEvent(INotification eventItem)
{
_domainEvents = _domainEvents ?? new List<INotification>();
_domainEvents.Add(eventItem);
}
public void RemoveDomainEvent(INotification eventItem)
{
_domainEvents?.Remove(eventItem);
}
public void ClearDomainEvents()
{
_domainEvents?.Clear();
}
public bool IsTransient()
{
return this.Id == default(Int32);
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Entity))
return false;
if (Object.ReferenceEquals(this, obj))
return true;
if (this.GetType() != obj.GetType())
return false;
Entity item = (Entity)obj;
if (item.IsTransient() || this.IsTransient())
return false;
else
return item.Id == this.Id;
}
public override int GetHashCode()
{
if (!IsTransient())
{
if (!_requestedHashCode.HasValue)
_requestedHashCode = this.Id.GetHashCode() ^ 31;
return _requestedHashCode.Value;
}
else
return base.GetHashCode();
}
public static bool operator ==(Entity left, Entity right)
{
if (Object.Equals(left, null))
return (Object.Equals(right, null)) ? true : false;
else
return left.Equals(right);
}
public static bool operator !=(Entity left, Entity right)
{
return !(left == right);
}
}
public interface IAggregateRoot
{}//its empty
测试用例1:使用Assert.Throws
[Fact]
public void add_friend_business_rule_validation_throws_exception1()
{
var me = new Me("mady");
var friend = new Friend("johnDoe","UnKnown");
Assert.Throws<Exception>(() => me.AddFriend(friend));
}
测试用例2:使用Record.Exception
[Fact]
public void add_friend_business_rule_validation_throws_exception()
{
var me = new Me("mady");
var friend = new Friend("johnDoe","UnKnown");
var ex = Record.Exception(() => me.AddFriend(friend));
Assert.NotNull(ex);
Assert.IsType<Exception>(ex);
}
我想编写一个单元测试用例来测试提供的代码的异常。我的期望是,在尝试将实体添加到集合时,代码应该在验证失败时失败。
问题:这里的测试用例并没有失败。执行在以下行停滞。
throw new Exception("Cannot add a unknown friend.");
当你没有尝试catch块来捕获它时会发生这种情况。我期待xUnit Assert.Throws&Record.Exception将捕获异常,但事实并非如此。
注意:提供的代码非常接近实际代码,通过删除不必要的代码,实体名称不同并简化。
我见过以下文档
编辑:xUnit Assert.Throws&Record.Exception行为是我运行测试时的预期行为。
问题在于调试测试。
如上所述,测试用例并没有失败。每当任何代码行抛出异常时,最近的catch块将捕获异常。我对xUnit Assert.Throws&Record.Exception有类似的期望,它基于文档和用户体验。但是在调试测试时情况并非如此。
另一种众所周知的方法来证明行为的问题和期望。
[Fact]
public void add_friend_business_rule_validation_throws_exception1()
{
var me = new Me("mady");
var friend = new Friend("johnDoe","UnKnown");
Exception expectedException = null;
try
{
me.AddFriend(friend); //exception thrown block
}
catch(Exception ex)//exception catched - control not stopped
{
expectedException = ex;
}
Assert.NotNull(expectedException);
Assert.IsType<Exception>(expectedException);
}
在运行测试时,xUnit Assert.Throws&Record.Exception行为与预期一致。
问题在于调试测试。
我可以通过从异常抛出的行中跳过来调试测试。当您看到控制器停在该线路上后,按F8或F5继续,将执行测试。
throw new Exception("Cannot add a unknown friend.");