2010-02-25 4 views
0

에 대한 AssertWasCalled 고정 I가이 같은 비트 (내 실제 문제에서 조금 단순화 해요) 보이는 시험 : 그 Monkey 처분하는 것을 제외하고,코뿔소 모의 객체 : 배치 객체

[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)); 
} 

이 잘 작동 것을 Banana 먹은 후. 따라서 바나나 객체는 더 이상 사용 가능한 상태가 아닙니다. 특히 구현은 해제 된 관리되지 않는 리소스를 사용하기 때문에 Dispose이 호출 된 후에는 작동하지 않습니다.

불행하게도 AssertWasCalled은 불려 Banana.Equals이 호출되어 테스트가 실패하게됩니다. 이 문제를 해결하는 가장 좋은 방법은 무엇입니까?

답변

0

는이 같은 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는 여전히 바나나가 이렇게 문제를 해결 배치되어있는 경우에도 작동합니다.

1

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); 
} 
+0

+1 테스트중인 클래스를 제외한 모든 것을 스텁하는 것이이 문제를 사라지게한다는 것을 이미 알고있었습니다. 불행히도 Monkey와 Banana는 추상화 될 수없는 interop wrappers입니다 - Monkey.Eat 메서드는 관리되는 래퍼를 관리되지 않는 동급 클래스로 전달하기 전에 "분리"하여 특정 클래스의 바나나를 사용합니다 유형. 기존의 관리되지 않는 코드와의 interop에 대한 필요성 때문에 적절한 TDD가 실제로 복잡해집니다. –

2

내가 객체를 인스턴스화하는 클래스는 폐기해야한다고 제안하는 경향이있을 것이다. 바나나를 원숭이에게 전달한 다음 소비 한 후에 바나나를 적절히 폐기하는 것이 좋습니다.

바나나를 인스턴스화하는 데 필요한 데이터를 바나나 자체가 아닌 원숭이에 전달할 수 있습니까? 또는 호출 클래스에서 처리 할 수 ​​있습니까?

+0

개체의 소유권을 이전 할 수 있습니다. 개체가 발생한 곳을 문서화하면됩니다. 그렇지 않으면 일회용 물체를위한 공장을 가질 수 없습니다. –

+0

예, 아마도 제 의견은 "인스턴스 생성을 요청하는 클래스"또는 이와 비슷한 표현으로 작성되어야합니다. 나는 일회용품을 인스턴스화하고 그것을 다른 클래스로 던져 버려서 그것 (공장 스타일)에 대해 보류를 해제하는 것이 좋습니다.내 문제는 처분이 필요한 클래스를 생성 한 다음 다른 클래스로 전달하는 것입니다. 특히이 클래스를 두 클래스 중 하나를 사용하여 객체를 삭제하려고 시도 할 수 있습니다. 그것으로 문제를 디버그하기는 어렵습니다. – pdr

0

몇 가지 가능한 해결 방법 :

  1. 어쩌면 당신은 당신의 주장을 휴식을 취할 수 : 정말 먹을 있었는지 바나나 있는지 확인해야합니까?
  2. OnConsume에 인라인 콜백을 사용하고 다른 과일이 아닌 바나나임을 주장하십시오. 이 콜백은 바나나가 처리되기 전에 실행됩니다.

지금 : 생각해 보면 코드에는 개념적인 문제가 있다고 생각합니다. OnConsume의 일부 구현이 바나나 껍질을 유지하고 싶지 않다는 것을 어떻게 알 수 있습니까?

+0

개념상의 문제는 없습니다. 바나나 계약의 일부는 비 소유자가 처분되기 전에 자신의 참조 권리를 포기할 수 있도록 Disposing 이벤트를 제기한다는 것입니다. –

+0

글쎄, 콜백을 도입하는 것이 여전히 당신의 문제를 해결하는 가장 쉬운 방법이다. 또는 메서드 호출에서 바나나가 제공되는지 확인하는 DummyConsumptionTracker를 구현할 수 있습니다. Disposing 이벤트가 발생했다고 주장하는 경우에도 마찬가지입니다. –

관련 문제