代码之家  ›  专栏  ›  技术社区  ›  Wim Coenen

Rhino模拟:为已释放的对象调用了修复断言

  •  0
  • Wim Coenen  · 技术社区  · 15 年前

    我有一个测试看起来有点像这样(我从我的实际问题简化了一点):

    [Test]
    public void Eat_calls_consumption_tracker_OnConsume()
    {
       var consumptionTrackerStub = 
          MockRepository.GenerateStub<IConsumptionTracker>();
       var monkey = new Monkey(consumptionTrackerStub);
       var banana = new Banana();
    
       monkey.Eat(banana);
    
       consumptionTrackerStub.AssertWasCalled(x => x.OnConsume(banana));
    }
    

    这会很好的,除了 Monkey 处置 Banana 吃了之后。因此,香蕉对象不再处于可用状态。尤其是, Banana.Equals 之后无法执行 Dispose 调用,因为它使用已释放的非托管资源。

    不幸地 AssertWasCalled 将导致 香蕉等于 被调用,爆炸,导致测试失败。解决这个问题的最佳方法是什么?

    4 回复  |  直到 15 年前
        1
  •  2
  •   pdr    15 年前

    我倾向于建议实例化对象的类应该处理它。把香蕉递给猴子,然后相信它在食用后能妥善处理,这绝对不是个好主意。

    你不能把实例化香蕉所需的数据传递给猴子,而不是香蕉本身吗?或者,您不能在调用类中处理它吗?

        2
  •  1
  •   TrueWill    15 年前

    如果monkey.eat的参数是ibanana(或ifood),而ibanana是idisposable的后代呢?然后您可以模拟ibanana,甚至验证是否调用了Dispose。

    编辑:你可以用 Moq :

    [Test]
    public void Eat_calls_consumption_tracker_OnConsume()
    {
        var consumptionTrackerStub = new Mock<IConsumptionTracker>();
        var monkey = new Monkey(consumptionTrackerStub.Object);
        var banana = new Banana();
        monkey.Eat(banana);
    
        consumptionTrackerStub.Verify(x => x.OnConsume(It.IsAny<Banana>()));
    }
    
    public class Banana
    {
        public override bool Equals(object obj)
        {
            throw new ObjectDisposedException("Banana");
        }
    }
    
    public class Monkey
    {
        public Monkey(IConsumptionTracker tracker)
        {
            _tracker = tracker;
        }
    
        public void Eat(object obj)
        {
            _tracker.OnConsume(obj);
        }
    
        private readonly IConsumptionTracker _tracker;
    }
    
    public interface IConsumptionTracker
    {
        void OnConsume(object obj);
    }
    
        3
  •  0
  •   Igor Brejc    15 年前

    几种可能的解决方案:

    1. 也许你可以放松你的断言:你真的需要检查一下吃的是香蕉吗?
    2. 在onconsume上使用内联回调,并断言它是香蕉,而不是其他水果。此回调将在释放香蕉之前执行。

    顺便问一句:现在我考虑一下,我认为您的代码有一个概念上的问题:您如何知道OnConsume的一些实现将不希望保留香蕉皮?

        4
  •  0
  •   Wim Coenen    15 年前

    结果证明你可以强迫犀牛嘲笑来核对论点 object.ReferenceEquals 这样地:

    [Test]
    public void Eat_calls_consumption_tracker_OnConsume()
    {
       var consumptionTrackerStub = 
          MockRepository.GenerateStub<IConsumptionTracker>();
       var monkey = new Monkey(consumptionTrackerStub);
       var banana = new Banana();
    
       monkey.Eat(banana);
    
       consumptionTrackerStub.AssertWasCalled(
          x => x.OnConsume(
             Arg<Banana>.Matches(
                y => object.ReferenceEquals(y, banana))));
    }
    

    object.referenceEquals 即使香蕉被处理了,仍然可以工作,从而解决了问题。