【问题标题】:How do I reset the result for a property in a stub without resetting the entire stub?如何在不重置整个存根的情况下重置存根中属性的结果?
【发布时间】:2009-11-04 10:10:43
【问题描述】:

我是 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); });
}

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

【问题讨论】:

    标签: c# nunit rhino-mocks


    【解决方案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); });
    }
    

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

    【讨论】:

      【解决方案2】:

      我可能遗漏了一些东西,但您是否尝试过这样做?

      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");
      

      【讨论】:

      • 是的。这正是我在CreateBasicIFooLikeObjectStub() 方法中所做的。问题是在我的测试方法中,我可能想调用stub.Stub( x =&gt; x.FooLikeObject2).Return(null); 以验证在这种情况下是否引发了异常。那是我得到上面详述的 InvalidOperationException 的时候。我将编辑问题以明确这一点。
      • @Lawrence 哦,我明白你在说什么。在这种情况下,您实际上需要做的是创建两个不同的存根实例并让它们具有不同的值集。
      猜你喜欢
      • 2011-11-30
      • 2018-02-17
      • 2017-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-13
      • 1970-01-01
      相关资源
      最近更新 更多