如何在不重置整个存根的情况下重置存根中属性的结果?

时间:2009-11-02 20:22:42

标签: c# nunit rhino-mocks

我是Rhino Mocks的新手,所以我可能完全错过了一些东西。

假设我的界面有六个属性:

public interface IFoo {
  string Foo1 { get; } // Required non-null or empty
  string Foo2 { get; } // Required non-null or empty
  string Foo3 { get; }
  string Foo4 { get; }
  int Foo5 { get; }
  int Foo6 { get; }
}

一个实现,它采用类似的对象但没有相同的约束并创建一个IFoo实例:

public interface IFooLikeObject {
  string FooLikeObject1 { get; } // Okay non-null or empty
  string FooLikeObject2 { get; } // Okay non-null or empty
  string FooLikeObject3 { get; }
  string FooLikeObject4 { get; }
  string FooLikeObject5 { get; } // String here instead of int
  string FooLikeObject6 { get; } // String here instead of int
}

public class Foo : IFoo {
  public Foo(IFooLikeObject fooLikeObject) {

    if (string.IsNullOrEmpty(fooLikeObject.Foo1)) {
      throw new ArgumentException("fooLikeObject.Foo1 is a required element and must not be null.")
    }

    if (string.IsNullOrEmpty(Foo2)) {
      throw new ArgumentException("fooLikeObject.Foo2 is a required element and must not be null")
    }

    // Make all the assignments, including conversions from string to int...

  }
}

现在,在我的测试中,我想测试在适当的时间抛出异常,以及在从字符串到int的失败转换期间抛出的异常。

所以我需要太多的IFooLikeObject来返回我当前没有测试的值的有效值,因为我不想在每个测试方法中复制这个代码,所以我把它提取出来用于单独的方法。

public IFooLikeObject CreateBasicIFooLikeObjectStub(MockRepository mocks) {
  IFooLikeObject stub = mocks.Stub<IFooLikeObject>();

  // These values are required to be non-null
  SetupResult.For(stub.FooLikeObject1).Return("AValidString");
  SetupResult.For(stub.FooLikeObject2).Return("AValidString2");
  SetupResult.For(stub.FooLikeObject5).Return("1");
  SetupResult.For(stub.FooLikeObject6).Return("1");
}

这足以测试Foo3和Foo4,但在测试Foo1,2,5或6时,我得到:

System.InvalidOperationException : The result for IFooLikeObject.get_FooLikeObject1(); has already been setup. Properties are already stubbed with PropertyBehavior by default, no action is required

例如:

[Test]
void Constructor_FooLikeObject1IsNull_Exception() {
  MocksRepository mocks = new MocksRepository();
  IFooLikeObject fooLikeObjectStub = CreateBasicIFooLikeObjectStub(mocks);

// This line causes the exception since FooLikeObject1 has already been set in CreateBasicIFooLikeObjectStub()
  SetupResult.For(fooLikeObjectStub.FooLikeObject1).Return(null); 

  mocks.ReplayAll();

  Assert.Throws<ArgumentException>(delegate { new Foo(fooLikeObjectStub); });
}

如何设置它以便我可以覆盖已经设置了返回值的单个属性,而不必重做所有其他属性?

2 个答案:

答案 0 :(得分:1)

这可以使用Repeat.Any()构造完成。

我没有使用SetupResult.For语法对此进行测试,但它适用于lambda语法:

public IFooLikeObject CreateBasicIFooLikeObjectStub(MockRepository) {
  IFooLikeObject stub = MockRepository.GenerateStub<IFooLikeObject>();

  // These values are required to be non-null
  stub.Stub(s => s.FooLikeObject1).Return("AValidString");
  stub.Stub(s => s.FooLikeObject2).Return("AValidString2");
  stub.Stub(s => s.FooLikeObject5).Return("1");
  stub.Stub(s => s.FooLikeObject6).Return("1");
}

[Test]
void Constructor_FooLikeObject1IsNull_Exception() {
  IFooLikeObject fooLikeObjectStub = CreateBasicIFooLikeObjectStub();

  // This line no longer causes an exception
  stub.Stub(s => s.FooLikeObject1).Return(null).Repeat.Any(); // The Repeat.Any() is key. Otherwise the value wont be overridden.

  Assert.Throws<ArgumentException>(delegate { new Foo(fooLikeObjectStub); });
}

我发现的唯一警告是你不能做两次。

答案 1 :(得分:0)

我可能会遗漏一些东西,但你试过这样做吗?

var stub = mocks.Stub<IFooLikeObject>();

stub.FooLikeObject1 = "AValidString";
stub.FooLikeObject2 = "AValidString2";
stub.FooLikeObject5 = "1";
stub.FooLikeObject6 = "1";

使用存根,您只需将属性设置为您希望它们直接存在的属性。

如果属性是只读的,你可以这样做:

var stub = mocks.Stub<IFooLikeObject>();

stub.Stub( x => x.FooLikeObject1).Return("AValidString");
stub.Stub( x => x.FooLikeObject2).Return("AValidString2");
stub.Stub( x => x.FooLikeObject5).Return("1");
stub.Stub( x => x.FooLikeObject6).Return("1");