【问题标题】:How to clear previous expectations on an object?如何清除以前对对象的期望?
【发布时间】:2009-04-20 20:44:10
【问题描述】:

我要设置返回值

_stubRepository.Stub(Contains(null)).IgnoreArguments().Return(true);

但随后在特定测试中,覆盖该期望以返回 false。

类似:

_stubRepository.ClearExpectations();  //<- this does not exist, I'm just making something up
_stubRepository.Stub(Contains(null)).IgnoreArguments().Return(false);

注意,我不希望期望在第二次调用时返回 false,我想覆盖第一个期望。

这将大大简化我的测试场景。

【问题讨论】:

    标签: .net rhino-mocks


    【解决方案1】:

    有三种方式:

    您可以使用 BackToRecord 重置预期

    我不得不承认我从来没有真正使用过它,因为它很尴尬。

    // clear expectations, an enum defines which
    _stubRepository.BackToRecord(BackToRecordOptions.All);
    // go to replay again.
    _stubRepository.Replay();
    

    编辑:现在我有时会使用它,它实际上是最干净的方式。应该有一个扩展方法(如 Stub)来完成它 - 我认为它只是被遗忘了。我建议你自己写。

    您可以使用 Repeat.Any()

    它“破坏”了存根定义的顺序并“覆盖”了以前的定义。但它在某种程度上是隐含的。我有时会使用它,因为它很容易编写。

    _stubRepository.Stub(x => x.Contains(null))
      .IgnoreArguments()
      .Return(false)
      .Repeat.Any();
    

    您可以创建一个新的模拟

    琐碎,但明确且易于理解。如果您想保留大量定义并且只更改一个调用,这只是一个问题。

    _stubRepository = MockRepository.GenerateMock<IRepository>();
    _stubRepository.Stub(x => x.Contains(null))
      .IgnoreArguments()
      .Return(false);
    

    【讨论】:

    • 在 Rhino Mock 内部说话,使用重复。 Any 创建了一个可重复的期望,它在播放过程中胜过正常的期望。不过,我建议使用 BackToRecord。
    • 啊,除了 Replay() 调用之外,一切都想通了。
    • 这是自 3.4 或更早版本使用 RhinoMocks 的人才真正知道的事情。 RhinoMocks 与 Record-Replay 一起使用,这意味着必须将模拟设置为显式重放模式。幸运的是,在 3.5 中,这已经消失了,模拟始终处于重放模式(至少对于自定义代码而言)。直到你把它放回记录模式——除了清除期望之外,我认为没有理由这样做。我已经想为这两行写一个补丁来轻松地重新设定期望。
    • public static void ResetExpectations(this T mock) where T : class { mock.BackToRecord(BackToRecordOptions.Expectations);模拟。重播();我有一堆方便的 rhino mocks 扩展,我应该把它们放在谷歌代码上..
    • 如果您在模拟对象上设置了多个期望值,并且只想重置其中一个,该怎么办? Repeat.Any 选项是唯一的吗?
    【解决方案2】:

    针对这些情况,我创建了一个简单的 RinoMocks 扩展方法,以更好地显示存根的意图并提高可读性。

    public static void OverridePrevious<T>(this IMethodOptions<T> options)
    {
        options.Repeat.Any();
    }
    

    因此,不要像下面这样可能需要评论的神秘调用:

    [SetUp]
    public void Setup()
    {
        carStub.Stub(x => x.Model).Return("Model1");
        carStub.Stub(x => x.Model).Return("Model2");
    }
    
    [Test]
    public void SomeTest()
    {
        //Arrange
        //overrides previous stubs that were setup for the Model property
        carStub.Stub(x => x.Model).Return(null).Repeat.Any();
    
        //Act
        //Assert
    }
    

    您可以获得更易读的测试,更好地显示 .Repeat.Any() 调用的意图:

    carStub.Stub(x => x.Model).Return(null).OverridePrevious();
    

    【讨论】:

    • 为了保持选项打开以链接存根配置,扩展方法不应该返回 IMethodOptions 吗? public static IMethodOptions&lt;T&gt; OverridePrevious&lt;T&gt;(this IMethodOptions&lt;T&gt; options) { return options.Repeat.Any(); }.
    • @PHeiberg - 我没有尝试过或需要保持“链条打开”,但我想你是对的。好点子。
    • 我喜欢 OverridePrevious 作为 void - 它强制它到最后。
    【解决方案3】:

    为了社区,我将把它添加到上面 Stefan 的选项列表中:

    如果需要经常更改返回值,我发现如下使用闭包既干净又高效。

    bool returnValue = true;
    _stubRepository.Stub(x => x.Contains(null)).IgnoreArguments().Do(new Func<bool>(() => {
        return returnValue;
    }));
    
    returnValue = false;
    // Calls to Contains now return false;
    
    returnValue = true;
    // Calls to Contains now return true;
    

    每次调用 Contains 时都会执行 lambda 表达式,因为我们创建了一个引用 returnValue 的闭包,所以它总是会查找 returnValue当前 值。

    【讨论】:

    • 这是我认为最干净的解决方案
    猜你喜欢
    • 2011-03-08
    • 2010-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    相关资源
    最近更新 更多