2011-09-02 5 views
5

이것은 내 첫 번째 질문이므로 친절하시기 바랍니다! :)반환 값이없는 메소드를 테스트하기 위해 Moq를 사용하는 방법은 무엇입니까?

내가하려고하는 것은 관리자 클래스에 대한 테스트를 작성하여 작성 중에 하나의 항목 클래스에 대한 많은 새 인스턴스를 목록에 추가하는 것입니다. 이 관리자 클래스에서 UpdateAllItems가 호출되면 목록을 반복하고 각 단일 항목에서 Increment를 호출합니다.

관리자 클래스는 내 코드이지만 단일 항목 클래스가 아니므로 수정할 수 없습니다.

테스트 프레임 워크에 NUnit을 사용하고 Moq로 작업하기 시작했습니다. 관리자 클래스는 단일 항목 클래스를 사용하기 때문에 관리자 만 테스트하므로 단일 항목이 아닌 Moq을 사용해야한다고 생각합니다.

어떻게하면 UpdateAllItems 메서드에 대한 테스트를 작성할 수 있습니까? (기술적으로 나는 먼저 내가 알고있는 테스트를 작성해야한다). 여기

는 ... 모든 도움에 미리

public class SingleItem_CodeCantBeModified 
{ 
    public int CurrentValue { get; private set; } 

    public SingleItem_CodeCantBeModified(int startValue) 
    { 
     CurrentValue = startValue; 
    } 

    public void Increment() 
    { 
     CurrentValue++; 
    } 
} 

public class SingleItemManager 
{ 
    List<SingleItem_CodeCantBeModified> items = new List<SingleItem_CodeCantBeModified>(); 

    public SingleItemManager() 
    { 
     items.Add(new SingleItem_CodeCantBeModified(100)); 
     items.Add(new SingleItem_CodeCantBeModified(200)); 
    } 

    public void UpdateAllItems() 
    { 
     items.ForEach(item => item.Increment()); 
    } 
} 

덕분에 내가 함께 일하고 어떤의 일반적인 아이디어를 제공하는 몇 가지 예제 코드입니다!

+0

이 클래스는 어떻게 유용합니까? 카운터의 개인 목록을 만들고 메서드가 호출 될 때마다 각 항목을 업데이트합니다. 객체의 동작에 약간의 변화가 있어야합니다. UpdateAllItems가 작동하지 않는 경우 - 관찰 된 변경 사항은 무엇입니까? – Gishu

답변

5

간단한 대답은 할 수 없습니다. UpdateAllItems (Increment())을 호출하는 방법은 가상이 아니므로 모의 할 수 없습니다.내가보기로

옵션은, 다음과 같습니다

  • 전혀 UpdateAllItems을 테스트하지 마십시오. 그것의 구현은 사소한 것이므로 이것은 (비록 이상적이지는 않지만) 고려해야 할 선택 사항이다.
  • 실제 SingleItem_CodeCantBeModified 인스턴스를 테스트에 만드십시오. 이 시점에서 유닛 테스트를 더 이상 수행하지 않는다고하지만, 여전히 유용한 테스트가 될 수 있습니다.
  • ISingleItem 인터페이스와 SingleItem_CodeCantBeModified에 대한 참조를 보유하는 SingleItemAdapter : ISingleItem 클래스를 추가하고 호출을 전달합니다. 그런 다음 SingleItemManager을 작성하여 ISingleItem에 대한 작업을 수행 할 수 있으며 테스트에서는 모의도 ISingleItem을 무료로 전달할 수 있습니다. (시스템 설정 방법에 따라, 당신도, SingleItem_CodeCantBeModified에서 내려 당신의 자손의 인터페이스를 구현하고, 어댑터를 작성하는 이러한 개체를 대신 사용할 수 있습니다.)

이 마지막 옵션은 당신에게를 제공한다는 대부분의 옵션이 있지만 약간의 복잡성이 있습니다. 성취하려는 것에 가장 적합한 옵션을 선택하십시오.

1

관리자가 항목에 너무 의존했습니다 (List<Item>). 그것을 모방 할 수있는 별도의 클래스로 목록 인구를 추출 할 수 있습니까? 예 :

public SingleItemManager() 
{ 
    items.Add(ItemRepository.Get(100)); 
    items.Add(ItemRepository.Get(200)); 
} 

테스트 (일부 코드 생략) :

int i = 0; 

var itemMock = new Mock<Item>(); 
itemMock.Setup(i => i.Increment()).Callback(() => i++); 

var repositoryMock = new Moc<ItemRepository>(); 
repositoryMock.Setup(r => r.Get(It.IsAny<int>()).Returns(itemMock.Object); 

var manager = new SingleItemManager(); 
manager.UpdateAllItems(); 

Assert.AreEqual(i, 1); 
-1

대신 하드 코딩, 귀하의 추가 구체적인 항목을 SingleItem_CodeCantBeModified이 인터페이스를 구현 (또는 인터페이스를 구현하는 래퍼를 포함) 그런 다음이 항목을 만드는 (새) 팩토리를 전달하십시오.

테스트에서는 Manager 클래스로 전달할 팩토리의 모의 객체를 생성 한 다음 해당 모의 객체에서 호출되는 메소드를 모니터링 할 수 있습니다.

하지만 이것은 부산물이 아닌 시스템 내부의 테스트에 관한 것입니다. Manager는 어떤 인터페이스를 구현합니까? 그것이 외부 적으로 증명되지 않는다면 어떤 결과를 테스트 할 것인가?

+0

SingleItem_ ** CodeCantBeModified ** – riezebosch

+0

@riezebosch : 밀어 넣기가 발생하면 interfact을 구현하고 메서드 호출을 통과하는 프록시 개체를 만들 수 있습니다. 인터페이스를 구현하기 위해 클래스를 gett하는 것과 똑같은 것은 아니지만 같은 효과가 있습니다. – Chris

0

평소와 같이 다른 수준의 간접 참조를 추가 할 수 있습니다.

  1. (Increment 가상 방법 인 경우이 래퍼 SingleItemManagerSingleItem_CodeCantBeModified

또는

대신 IItem에 따라

  • 확인 IItem 인터페이스를 상속합니다 SingleItem_CodeCantBeModified
  • 래퍼 클래스를 생성 샘플 코드가 아니라는 것을 이해합니다. 다만의 경우) partial mocking을 사용하십시오.

  • 관련 문제