用moq测试这个的正确方法

时间:2012-01-12 05:45:04

标签: c# unit-testing moq

我为我需要的优先级队列编写了一个实现,现在我想测试它。我决定使用moq,因为我已经在工作中使用过犀牛嘲讽,并想尝试一些新的/可能更容易的东西。

我的PriorityQueue界面非常简单:

public interface IPriorityQueue<TKey, TValue>  where TKey : IComparable
{
    void Enqueue(TKey priority, TValue value);
    bool IsEmpty();
    TValue Peek();
    TValue Dequeue();
}

我去写了我的第一个测试,它测试了Enqueue方法。以下是它的实现:

public void Enqueue(TKey priority, TValue value)
{
    if (priority == null) { throw new ArgumentNullException("priority"); }

    if (_queue.ContainsKey(priority))
    {
        // queue contains key, therefore just add value to existing key's list
        _queue[priority].Add(value);
    }

    // otherwise just add pair
    else
    {
        _queue.Add(priority, new List<TValue>() { value });
    }
}

我写的第一个单元测试是测试密钥是否为null,它应该抛出一个参数null异常。

[TestMethod]
public void EnqueueNullKey_ThrowsArgumentNullException()
{
    /* Arrange */
    var mock = new Mock<IPriorityQueue<string, IMessage>>();

    // string implements the IComparable interface, and is nullable.
    mock
        .Setup(x => x.Enqueue(null, It.IsAny<IMessage>()))
        .Throws<ArgumentNullException>();

    /* Assert */
    mock.VerifyAll();   
}

所以我现在意识到,我的方法Enqueue永远不会被调用,因为我实例化了我的接口实例,而不是实现。然后问题就是要问,如果我应该使用我的界面进行测试(至少这是我在观看Roy Osherove的TDD - Understanding Mock Objects视频后得到的印象)我如何测试我的实现?

我是否误解了使用接口进行测试的建议?

在视频中,他在他正在编写的测试中创建了一个类,并用它进行测试。我不知道这将如何帮助我测试我的PriorityQueue(特别是Enqueue方法)的实现。

感谢堆栈溢出!

编辑:这是我提出的以下(丑陋)工作测试。我对它非常不满意,感觉很原始。谁能建议更好的方法呢?从下面的回答来看,这个单元测试似乎完全没有框架。

但是,这是:

[TestMethod]
public void EnqueueNullKey_ThrowsArgumentNullException()
{
    /* Arrange */
    var pq = new PriorityQueue<string, IMessage>();

    try
    {
        pq.Enqueue(null, null);
    }
    catch(ArgumentNullException)
    {
        Assert.IsTrue(true);
        return;
    }

    // failure condition if we don't catch the exception
    Assert.IsTrue(false);   
}

3 个答案:

答案 0 :(得分:4)

我认为存在一些误解。

如果您的测试对象具有依赖项(定义为接口),您可以模拟它们以便能够测试您的单元。

在您的情况下,您没有需要替换以进行测试的依赖项。由于您希望测试您的实现,因此无需模拟它。恕我直言,没有必要测试现有的BCL类。

测试(Nunit)测试看起来像这样(未经测试)

[TestMethod]
public void EnqueueNullKey_ThrowsArgumentNullException()
{
   var queue = new YourPriorityQueue<string, IMessage>();
   var message = new SomeFooMessage(); // or use a mock for this

   Assert.Throws(typeof(ArgumentNullException), () => queue.Enqueue(null, message);
}

答案 1 :(得分:2)

你弄错了。当您在其中一个实现上测试方法时,您不需要模拟IPriorityQueue。你需要的是模拟queue

Enqueue方法的职责是

  1. 如果优先级为空则抛出异常
  2. 在条件
  3. 上做某事
  4. 在其他条件下做某事
  5. 对于#1测试非常简单,queue的存根实现就足够了,对于#2和#3,你需要模拟queue。对于#2和#3,您应该测试是否使用正确的参数调用模拟队列的Add方法

答案 2 :(得分:1)

您可以使用[ExceptionExpected]属性。

[TestMethod]
[ExpectedException(ExpectedException = typeof(ArgumentNullException)]
public void EnqueueNullKey_ThrowsArgumentNullException()
{
    /* Arrange */
    var pq = new PriorityQueue<string, IMessage>();

    /* Act */
    pq.Enqueue(null, null);
}

如果未抛出ArgumentNullException,测试将失败。