2

给定一个预期会抛出异常的构造函数:

public class MyObject
{
  public MyObject(String name)
  {
    if (String.IsNullOrEmpty(name))
      throw new ArgumentNullException("name");

    this.Initialize();
  }

  protected virtual void Initialize()
  {
    // do stuff
  }
}

我将如何使用 Machine.Fakes 和 Rhino(默认设置,我正在迁移到 Rhino)来模拟这个类,并测试它:

  1. 它抛出预期的异常
  2. 它不调用 Initialize()

使用 Moq,我可以模拟实际的 MyObject 类本身并在模拟上设置属性Callbase = true以使其像普通类一样工作。

然后,我可以验证是否引发了异常,并且没有使用以下方法调用该方法:

// all pseudo code to prove my point of "creating an instance"
//
void when_creating_new_MyObject_with_null_Name_should_throw_Exception()
{
  // arrange
  Mock<MyObject> myObjectToTest = new Mock<MyObject>(String.Empty);
  myObjectToTest.Callbase = true;

  // act
  Assert.Throws<ArgumentNullException>(() => 
    var instance = myObjectToTest.Object;
  );
}

void when_creating_new_MyObject_with_null_Name_should_not_call_Initialize()
{
  // arrange
  Mock<MyObject> myObjectToTest = new Mock<MyObject>(String.Empty);
  myObjectToTest.Callbase = true;

  // act
  try
  {
    // creates an instance
    var instance = myObjectToTest.Object;
  }
  catch {}

  // assert
  myObjectToTest.Verify(x => x.Initialize(), Times.Never());
}

但我无法弄清楚如何使用带有 Fakes 的 MSpec 来模拟它:

[Subject(typeof(MyObject), "when instantiating a new instance")
class with_null_Name
{
  static MyObject myObjectToTest;
  static Exception expectedException;

  Establish context =()=>
    myObjectToTest = An<MyObject>(String.Empty);

  Because of;  // I don't think there is anything to act on here?

  It should_throw_Exception;
    // how to capture exception with An<T>()?

  It should_not_call_Initialize = () =>
    myObjectToTest.WasNotToldTo(x => x.Initialize());

}

我确实知道并Catch.Exception(...)在我的Because of行为中正常使用。但是这个用例似乎不适用于那个。

任何指针将不胜感激。

谢谢!

免责声明:现实世界的用例非常复杂,需要初始化大量对象,缓存的支持成员非常昂贵。上面的代码只是一个简化版本。

4

2 回答 2

3

对我来说,测试一个 mocked 没有意义sut,模拟行为是由你在测试方法中定义的。你应该测试真实的对象。
在这种情况下,您应该只验证在提供错误数据时是否引发了异常。
我想你知道该怎么做,但无论如何这里是代码:

[Subject(typeof (MyObject), "when instantiating a new instance")]
internal class with_null_Name
{
    static MyObject myObjectToTest;
    static Exception expectedException;

    Because of = () => expectedException = Catch.Exception(
                    () => myObjectToTest = new MyObject(String.Empty));

    It should_fail = () => expectedException.ShouldNotBeNull();
}
于 2013-07-25T15:22:49.343 回答
2

就个人而言,我不习惯使用模拟框架来模拟被测单元内的东西。我使用 mocks 使用依赖注入来打破外部依赖。我绝不是这方面的专家,但我的直觉是,这是一个太细粒度的细节,无法测试。

你说你的现实世界有很多对象要初始化;你不能模拟一个或多个这些对象并检查它们是否没有被调用吗?或者,使用依赖注入注入一些可以设置标志的假对象。

这些建议对我来说似乎都很臭。最后,您知道您是否抛出了未调用 Initialize 的异常。我想我理解您正在尝试确保在将来的编辑中不会出现错误,但您实际上是在对开发人员进行单元测试吗?我个人认为在提供错误数据时验证是否引发异常就足够了。我有兴趣听到任何不同的意见。

于 2013-07-24T16:29:49.993 回答