NUnit CollectionAssert.AreEqual(expected,actual) vs Assert.IsTrue(expected.SequenceEqual(actual))

Posted

技术标签:

【中文标题】NUnit CollectionAssert.AreEqual(expected,actual) vs Assert.IsTrue(expected.SequenceEqual(actual))【英文标题】: 【发布时间】:2014-12-11 00:37:48 【问题描述】:

我有一些放入队列的对象。队列中的所有对象都实现相同的基接口,这也要求它们实现 IEquatable。

我想验证正确的对象是否以正确的顺序放置在队列中。

当我编写一个断言 CollectionAssert.AreEqual(expected,actual) 的测试时,测试失败,说明虽然队列是预期长度,但它在索引 0 处有所不同。

但是,当我将测试断言写为Assert.IsTrue(expected.SequenceEqual(actual)) 时,测试通过了。

在我看来,这两个断言应该是相同的,但显然它们是完全不同的。

NUnit 的网站对集合断言方法不是很详细,而 Linq 文档虽然更详细,但如果不能与 NUnit 对集合断言方法所做的事情进行比较,就没有真正的帮助。

这两个断言有何不同?

编辑 - 添加细节

这是正在测试的代码以及我希望得到相同结果的两个测试。

public enum ArgumentTypeEnum

    SpiceworksDbName,
    DestinationDbName,
    DestinationServerName


public interface IArgument : IEquatable<IArgument>



public interface ICommandArgument : IArgument

    ArgumentTypeEnum ArgumentType  get; 


internal interface IValueArgument : IArgument

    String ArgumentValue  get; 


public class CommandArgument : ICommandArgument

    public CommandArgument(ArgumentTypeEnum argumentType) 
        ArgumentType = argumentType;
    

    public ArgumentTypeEnum ArgumentType  get; private set; 

    public bool Equals(IArgument other)
    
        if (other == null)
        
            return false;
        

        var otherAsCommandArg = other as CommandArgument;
        if (otherAsCommandArg == null)
        
            return false;
        

        return ArgumentType == otherAsCommandArg.ArgumentType;
    


public class ValueArgument : IValueArgument

    public ValueArgument(String argumentValue)
    
        ArgumentValue = argumentValue;
    

    public string ArgumentValue  get; private set; 

    public bool Equals(IArgument other)
    
        if (other == null)
        
            return false;
        

        var otherAsValueArg = other as ValueArgument;
        if (otherAsValueArg == null)
        
            return false;
        

        return ArgumentValue == otherAsValueArg.ArgumentValue;
    


public class Tokenizer

     public Queue<IArgument> TokenizeArguments(string[] args) 
        var tokenQueue = new Queue<IArgument>();
        args.ToList().ForEach((arg) => 
                if (arg.StartsWith("-"))
                
                    switch (arg)
                    
                        case "-sd":
                            tokenQueue.Enqueue(new CommandArgument(ArgumentTypeEnum.SpiceworksDbName));
                            break;
                        case "-dd":
                            tokenQueue.Enqueue(new CommandArgument(ArgumentTypeEnum.DestinationDbName));
                            break;
                        case "-ds":
                            tokenQueue.Enqueue(new CommandArgument(ArgumentTypeEnum.DestinationServerName));
                            break;
                        default:
                            tokenQueue.Enqueue(new ValueArgument(arg));
                            break;
                    
                
                else
                
                    tokenQueue.Enqueue(new ValueArgument(arg));
                
            );
        return tokenQueue;
    


[TestFixture]
public class TokenizerSpecs

    ///Passes
    [Test] 
    public void Tokenizer_MultipleCommandsAndValues_TokenizesAsExpected1() 
        var args = new[]
        
            "-sd", @"\\some\Directory\foo.db", "-dd", "some database name", "-ds", "Server.name", "somerandomarg",
            "-unreconized"
        ;
        var t = new Tokenizer();
        var actualResult = t.TokenizeArguments(args);

        var expectedResult = new Queue<IArgument>();
        expectedResult.Enqueue(new CommandArgument(ArgumentTypeEnum.SpiceworksDbName));
        expectedResult.Enqueue(new ValueArgument(@"\\some\Directory\foo.db"));
        expectedResult.Enqueue(new CommandArgument(ArgumentTypeEnum.DestinationDbName));
        expectedResult.Enqueue(new ValueArgument("some database name"));
        expectedResult.Enqueue(new CommandArgument(ArgumentTypeEnum.DestinationServerName));
        expectedResult.Enqueue(new ValueArgument("Server.name"));
        expectedResult.Enqueue(new ValueArgument("somerandomarg"));
        expectedResult.Enqueue(new ValueArgument("-unreconized"));

        Assert.IsTrue(expectedResult.SequenceEqual(actualResult));
    

    ///Fails
    [Test]
    public void Tokenizer_MultipleCommandsAndValues_TokenizesAsExpected2()
    
        var args = new[]
        
            "-sd", @"\\some\Directory\foo.db", "-dd", "some database name", "-ds", "Server.name", "somerandomarg",
            "-unreconized"
        ;
        var t = new Tokenizer();
        var actualResult = t.TokenizeArguments(args);

        var expectedResult = new Queue<IArgument>();
        expectedResult.Enqueue(new CommandArgument(ArgumentTypeEnum.SpiceworksDbName));
        expectedResult.Enqueue(new ValueArgument(@"\\some\Directory\foo.db"));
        expectedResult.Enqueue(new CommandArgument(ArgumentTypeEnum.DestinationDbName));
        expectedResult.Enqueue(new ValueArgument("some database name"));
        expectedResult.Enqueue(new CommandArgument(ArgumentTypeEnum.DestinationServerName));
        expectedResult.Enqueue(new ValueArgument("Server.name"));
        expectedResult.Enqueue(new ValueArgument("somerandomarg"));
        expectedResult.Enqueue(new ValueArgument("-unreconized"));

        CollectionAssert.AreEqual(expectedResult, actualResult);
    

【问题讨论】:

对两个包含IEquatable&lt;&gt;s 的Queues 进行快速测试并不能确认您的发现:两种方法一致通过或失败。也许你应该展示更多细节。 听起来不错。稍后我会添加更多细节。 【参考方案1】:

我查看了 NUnit 的源代码,您似乎在这里遇到了错误。比较由NUnitEqualityComparer.EnumerablesEqual 完成,随后NUnitEqualityComparer.ObjectsEqual 比较每个元素。我们可以使用这个对象做简单的测试:

var n = new NUnitEqualityComparer();
var tolerance = Tolerance.Zero;
var equal = n.AreEqual(new CommandArgument(ArgumentTypeEnum.SpiceworksDbName),
                       new CommandArgument(ArgumentTypeEnum.SpiceworksDbName),
                       ref tolerance);

这个测试返回false

不知何故,ObjectsEqual 不会出现在对象的 Equals 方法中。需要对源代码进行调试才能找出原因,但这个测试足以证明存在错误。

【讨论】:

非常有趣!那么我提出的方法(使用 SequenceEqual)是目前安全的方法吗?知道这个错误的限制可能是什么吗?您认为该问题是否会影响所有实现 IEquatable 的对象的所有比较? 似乎如此。知道这一点,我当然会使用SequenceEqual。我无法理解这个错误的影响,但它看起来很严重。 我在 NUnit Github 页面上提交了一个 issue 来回应这个问题。

以上是关于NUnit CollectionAssert.AreEqual(expected,actual) vs Assert.IsTrue(expected.SequenceEqual(actual))的主要内容,如果未能解决你的问题,请参考以下文章

NUnit属性-百度Nunit-Gui

如何使用 NUnit 3 在 Atlassian Bamboo 中运行 NUnit Runner?

使用参数并行运行 nunit 测试 (nunit 3.8.x)

nUnit 和 Azure - 如何从 nUnit 启动 Dev Fabric

单元测试之NUnit二

Nunit配置