C# 测试集合不包含多个成员
考虑以下代码:C# 测试集合不包含多个成员,c#,collections,nunit,assertion,nunit-3.0,C#,Collections,Nunit,Assertion,Nunit 3.0,考虑以下代码: const string user8 = "user8"; const string user9 = "user9"; string[] users = { "user1", "user2", "user3", "user4", user8 }; 我想测试用户是否不包含user8或user9。我以前用过 Assert.That(users, Is.Not.SupersetOf(new[] {user8, user9 })); 不幸的是,它通过了测试(这不是预期的)。我可以用
const string user8 = "user8";
const string user9 = "user9";
string[] users = { "user1", "user2", "user3", "user4", user8 };
我想测试用户
是否不包含user8或user9。我以前用过
Assert.That(users, Is.Not.SupersetOf(new[] {user8, user9 }));
不幸的是,它通过了测试(这不是预期的)。我可以用
Assert.That(users, Does.Not.Contains(user8).And.Not.Contains(user9));
但如果我打算对超过2个成员测试集合,这将是一个问题。有更好的语法吗?我用的是NUnit3.4
注意:目标不仅仅是测试结果,还应该是正确的断言,这样无论何时测试失败,我们都可以从错误消息中更快地确定。这是上一个示例中的错误消息示例(使用dos.Not.Contains
)
应为:不包含“user8”和“user9”的集合
但是是:<“user1”、“user2”、“user3”、“user4”、“user8”>“尝试使用
CollectionAssert.IsNotSubsetOf()
更新:
嗯,你总是可以使用基本循环
Array.ForEach(new[] { user8, user9 }, u => Assert.That(users, Has.No.Member(u)));
这将通过循环检查users
是否在new[]{user8,user9}
数组中包含任何实例
错误消息如下所示:
应为:不包含“user8”的集合
但是是:<“用户1”、“用户8”、“用户2”、“用户3”>
尝试使用
CollectionAssert.IsNotSubsetOf()
更新:
嗯,你总是可以使用基本循环
Array.ForEach(new[] { user8, user9 }, u => Assert.That(users, Has.No.Member(u)));
这将通过循环检查users
是否在new[]{user8,user9}
数组中包含任何实例
错误消息如下所示:
应为:不包含“user8”的集合
但是是:<“用户1”、“用户8”、“用户2”、“用户3”>
尝试根据用户检查排除列表
const string user8 = "user8";
const string user9 = "user9";
string[] users = { "user1", "user2", "user3", "user4", user8 };
Assert.Multiple(() => {
var exclude = new[] { user8, user9 };
foreach(var user in exclude) {
Assert.That(users, Has.No.Member(user));
}
}
此测试应失败,因为用户列表中确实包含user8
NUnit文档-尝试根据用户检查排除列表
const string user8 = "user8";
const string user9 = "user9";
string[] users = { "user1", "user2", "user3", "user4", user8 };
Assert.Multiple(() => {
var exclude = new[] { user8, user9 };
foreach(var user in exclude) {
Assert.That(users, Has.No.Member(user));
}
}
此测试应失败,因为用户列表中确实包含user8
NUnit文档-在深入了解了创建自定义约束和下载NUnit源代码之后,我决定创建自定义集合ContainsConstraint a
/// <summary>
/// CollectionContainsAnyConstraint is used to test whether a collection
/// contains any member in expected collection.
/// </summary>
/// <typeparam name="T"></typeparam>
public class CollectionContainsAnyConstraint<T> : CollectionContainsConstraint
{
public CollectionContainsAnyConstraint(IEnumerable<T> expected) : base(expected)
{
}
public override string Description
=> Regex.Replace(base.Description, @"^\s*collection containing", "collection containing any of");
/// <summary>
/// Test whether any member in expected collection is available in actual collection
/// </summary>
/// <param name="actual">Actual collection</param>
/// <returns></returns>
protected override bool Matches(IEnumerable actual)
{
var convertedExpected = (IEnumerable<T>)Expected;
var convertedActual = EnsureHasSameGenericType(actual, typeof(T));
return convertedActual.Any(x => convertedExpected.Contains(x));
}
private IEnumerable<T> EnsureHasSameGenericType(IEnumerable actual, Type expectedType)
{
var sourceType = actual.GetType();
var sourceGeneric = sourceType.IsArray
? sourceType.GetElementType()
: sourceType.GetGenericArguments().FirstOrDefault();
if (sourceGeneric == null)
throw new ArgumentException("The actual collection must contain valid generic argument");
if (!sourceGeneric.Equals(expectedType))
throw new ArgumentException($"The actual is collection of {sourceGeneric.Name} but the expected is collection of {expectedType.Name}");
return (IEnumerable<T>)actual;
}
}
public static class ConstraintExtensions
{
/// <summary>
/// Returns a new <see cref="CollectionContainsAnyConstraint{T}"/> checking for the
/// presence of any object of the <see cref="expected"/> collection against actual collection.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
/// <param name="expected">A collection where one of its member is available in actual collection</param>
/// <returns></returns>
public static CollectionContainsAnyConstraint<T> ContainsAny<T>(this ConstraintExpression expression,
params T[] expected)
{
var constraint = new CollectionContainsAnyConstraint<T>(expected);
expression.Append(constraint);
return constraint;
}
}
//
///CollectionContainesAnyConstraint用于测试集合
///包含预期集合中的任何成员。
///
///
公共类CollectionContainsAnyConstraint:CollectionContainsConstraint
{
公共集合containsAnyConstraint(预期为IEnumerable):基本(预期为)
{
}
公共重写字符串描述
=>Regex.Replace(base.Description,@“^\s*包含的集合”,“包含任何项的集合”);
///
///测试预期集合中的任何成员是否在实际集合中可用
///
///实际收款
///
受保护的覆盖布尔匹配(IEnumerable实际)
{
var convertedExpected=(IEnumerable)预期值;
var convertedActual=确保安全性平均类型(实际,类型(T));
返回convertedActual.Any(x=>convertedExpected.Contains(x));
}
private IEnumerable EnsurePassaMegenericType(IEnumerable实际类型,expectedType类型)
{
var sourceType=actual.GetType();
var sourceGeneric=sourceType.IsArray
?sourceType.GetElementType()
:sourceType.GetGenericArguments().FirstOrDefault();
if(sourceGeneric==null)
抛出新ArgumentException(“实际集合必须包含有效的泛型参数”);
如果(!sourceGeneric.Equals(expectedType))
抛出新ArgumentException($“实际是{sourceGeneric.Name}的集合,但预期是{expectedType.Name}的集合”);
返回(IEnumerable)实际值;
}
}
公共静态类约束
{
///
///返回一个新的检查
///存在与实际集合相对的集合的任何对象。
///
///
///
///其成员之一在实际集合中可用的集合
///
公共静态集合containsAnyConstraint ContainsAny(此约束表达式,
参数T[]预期值)
{
var约束=新集合包含SANYCONSTRAINT(预期);
expression.Append(约束);
回报约束;
}
}
用法如下所示
[TestFixture]
class GivenCustomCollectionContainsTest
{
const string User8 = "user8";
const string User9 = "user9";
private readonly List<string> users = new List<string> { "user1", "user2", "user3", "user4", "user5" };
[Test]
public void WhenActualContainsOneOfExpectedAndPreceededByNotOperatorThenItShouldFail()
{
var actual = users.ToList();
actual.Add(User8);
var assert = Assert.Throws<AssertionException>(() => Assert.That(actual, Does.Not.ContainsAny(User8, User9)));
Assert.That(assert.ResultState.Status, Is.EqualTo(TestStatus.Failed));
Assert.That(assert.Message, Does.Contain("not collection containing any of").And.Contain($"\"{User8}\""));
}
[Test]
public void WhenActualContainsAllOfExpectedAndPreceededByNotOperatorThenItShouldFail()
{
var actual = users.ToList();
actual.Add(User8);
actual.Add(User9);
var assert = Assert.Throws<AssertionException>(() => Assert.That(actual, Does.Not.ContainsAny(User8, User9)));
Assert.That(assert.ResultState.Status, Is.EqualTo(TestStatus.Failed));
Assert.That(assert.Message, Does.Contain("not collection containing any of").And.Contain($"\"{User8}\", \"{User9}\""));
}
}
[TestFixture]
类GivenCustomCollectionContainsTest
{
常量字符串User8=“User8”;
常量字符串User9=“User9”;
私有只读列表用户=新列表{“user1”、“user2”、“user3”、“user4”、“user5”};
[测试]
当实际包含预期的和之前的不可操作项应失败()时,公共无效
{
var-actual=users.ToList();
实际添加(User8);
var assert=assert.Throws(()=>assert.That(实际,不.Not.ContainsAny(User8,User9));
Assert.That(Assert.ResultState.Status,Is.EqualTo(TestStatus.Failed));
Assert.That(Assert.Message,Does.Contain(“不包含任何内容的集合”).And.Contain($“\{User8}\”);
}
[测试]
当实际包含预期的分配和之前的不可操作项应失败()时,公共无效
{
var-actual=users.ToList();
实际添加(User8);
添加(User9);
var assert=assert.Throws(()=>assert.That(实际,不.Not.ContainsAny(User8,User9));
Assert.That(Assert.ResultState.Status,Is.EqualTo(TestStatus.Failed));
Assert.That(Assert.Message,Does.Contain(“不包含任何内容的集合”).And.Contain($“\”{User8}\,\“{User9}\”));
}
}
请注意,这仅用于阴性测试。幸运的是,我现在不需要测试“一个集合的任何成员在另一个集合中都可用”。大多数情况下,它测试一个集合是否是另一个集合的一部分,因此,在深入了解创建自定义约束和下载NUnit源代码后,我可以使用
is.SupersetOf
,因此我决定创建自定义集合contains约束
/// <summary>
/// CollectionContainsAnyConstraint is used to test whether a collection
/// contains any member in expected collection.
/// </summary>
/// <typeparam name="T"></typeparam>
public class CollectionContainsAnyConstraint<T> : CollectionContainsConstraint
{
public CollectionContainsAnyConstraint(IEnumerable<T> expected) : base(expected)
{
}
public override string Description
=> Regex.Replace(base.Description, @"^\s*collection containing", "collection containing any of");
/// <summary>
/// Test whether any member in expected collection is available in actual collection
/// </summary>
/// <param name="actual">Actual collection</param>
/// <returns></returns>
protected override bool Matches(IEnumerable actual)
{
var convertedExpected = (IEnumerable<T>)Expected;
var convertedActual = EnsureHasSameGenericType(actual, typeof(T));
return convertedActual.Any(x => convertedExpected.Contains(x));
}
private IEnumerable<T> EnsureHasSameGenericType(IEnumerable actual, Type expectedType)
{
var sourceType = actual.GetType();
var sourceGeneric = sourceType.IsArray
? sourceType.GetElementType()
: sourceType.GetGenericArguments().FirstOrDefault();
if (sourceGeneric == null)
throw new ArgumentException("The actual collection must contain valid generic argument");
if (!sourceGeneric.Equals(expectedType))
throw new ArgumentException($"The actual is collection of {sourceGeneric.Name} but the expected is collection of {expectedType.Name}");
return (IEnumerable<T>)actual;
}
}
public static class ConstraintExtensions
{
/// <summary>
/// Returns a new <see cref="CollectionContainsAnyConstraint{T}"/> checking for the
/// presence of any object of the <see cref="expected"/> collection against actual collection.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
/// <param name="expected">A collection where one of its member is available in actual collection</param>
/// <returns></returns>
public static CollectionContainsAnyConstraint<T> ContainsAny<T>(this ConstraintExpression expression,
params T[] expected)
{
var constraint = new CollectionContainsAnyConstraint<T>(expected);
expression.Append(constraint);
return constraint;
}
}
//
///集合包含一个CONST