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

时间:2014-10-15 19:54:09

标签: c# linq collections nunit assertions

我有一些对象被放入队列中。队列中的所有对象都实现相同的基接口,这也要求它们实现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);
    }
}

1 个答案:

答案 0 :(得分:3)

我看了一下NUnit的源代码,看来你这里有个bug。比较由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方法结束。它需要针对源代码进行调试以找出原因,但是这个测试足以证明涉及到一个bug。