2012-06-04 3 views
20

RhinoMocks에서 모의 ​​객체를 담요 문으로 IgnoreArguments로 말할 수 있습니다. Moq에서는 각 인수에 대해 It.IsAny()를 지정해야합니다. 그러나 이것은 ref 및 out 인수에 대해서는 작동하지 않습니다.Moq가 참조 또는 무시하는 인수를 무시하는 방법

public void MyMethod() { 
    // DoStuff 

    IList<SomeObject> errors = new List<SomeObject>(); 
    var result = _service.DoSomething(ref errors, ref param1, param2); 

    // Do more stuff 
} 

시험 방법 :

public void TestOfMyMethod() { 
    // Setup 
    var moqService = new Mock<IMyService>(); 
    IList<String> errors; 
    var model = new MyModel(); 

    // This returns null, presumably becuase "errors" 
    // here does not refer to the same object as "errors" in MyMethod 
    moqService.Setup(t => t.DoSomething(ref errors, ref model, It.IsAny<SomeType>()). 
     Returns(new OtherType())); 
} 

UPDATE : 그래서, "심판"에서 오류 변화에 내가 특정 결과를 반환하기 위해 내부 서비스 호출을 MOQ을 필요로하는 곳에 나는 다음과 같은 방법을 테스트 할 수있는 방법 "밖으로"작동합니다. 실제 문제는 주입 할 수없는 ref 매개 변수를 갖는 것 같습니다.

+0

당신 때문에 샘플이 3 인자'초과 오류, PARAM1, param2'을 가지고 있지만 테스트에서 두 개의 인수를 호출하는'초과 오류, It.IsAny 에 DoSomething''의 서명을 게시 할 수()'어쩌면 코드가 작동하기 때문에 잘못된 오버로드를 조롱하고있을 수도 있습니다. [moq help methods section] (http://code.google.co.kr/p/moq/wiki/QuickStart). – nemesv

+0

그건 그냥 예제였다 -하지만 난 예제 코드를 병렬로 테스트를 업데이 트했습니다 – sydneyos

답변

14

이미 알았 으면 문제는 ref 인수와 함께 있습니다.

moq는 현재 ref 인수와 정확히 일치합니다. 즉, Setup에서 사용한 것과 동일한 인스턴스를 전달하면 호출 만 일치합니다. 따라서 일치하지 않으므로 It.IsAny()이 작동하지 않습니다.

참조 MOQ quickstart

// ref arguments 
var instance = new Bar(); 
// Only matches if the ref argument to the invocation is the same instance 
mock.Setup(foo => foo.Submit(ref instance)).Returns(true); 

그리고 MOQ discussion group :

참조 매칭 설정이 방법은 동일한 인스턴스라는 경우에만 일치하는 것을 의미한다. It.IsAny는 null을 반환하므로 아마도 이 아닙니다.

설치시 실제 호출과 동일한 인스턴스를 사용하고 설치가 일치합니다.

+1

그래서 ref/out 변수가 시도하고있는 전화를 만드는 방법 안에 설정되어있는 중첩 된 호출이있는 경우 조롱, 나는 붙어있다. 이 제한이없는 조롱 프레임 워크에 대한 제안 사항은 무엇입니까? – sydneyos

+0

RhinoMocks IgnoreArguments() 옵션이이를 수행해야한다고 생각합니다. 시도해 보겠습니다. – sydneyos

+0

업데이트, 이러한 상황에서 우리는 RhinoMocks로 전환해야했습니다. 구현은 다음과 같습니다. IList errors; _repository.Stub (t => t.MethodName (오류)) .OutRef (새 목록 ()) .I IgnoreArguments(); – sydneyos

2

앞서 언급 한 @nemesv와 마찬가지로 It.IsAny는 null을 반환하므로 ref 매개 변수로 사용할 수 없습니다. 호출이 작동하려면 실제 객체를 호출해야합니다.

ref를 통해 전달하려는 개체를 만들 수없는 경우 문제가 발생합니다. 실제 객체에 액세스 할 수 없다면 테스트에서이 객체를 사용하고 모의하려고하지 않아도됩니다.

바로 여기에서 Extract and Override 기술을 사용하여 해결할 수 있습니다. 이름에서 알 수 있듯이 문제가되는 코드 비트를 자체 메서드로 추출합니다. 그런 다음 테스트중인 클래스에서 상속 된 테스트 클래스의 메서드를 재정의합니다. 마지막으로 실제 객체를 설정하고 새로 작성한 테스트 클래스에 전달한 다음 원하는대로 ref 호출을 테스트합니다.

길고 (고안된) 코드는 오래되었지만 코드의 전후를 보여주고 마지막에 통과 테스트가 있습니다.

using System; 
using System.Collections.Generic; 
using Moq; 
using MoqRefProblem; 
using NUnit.Framework; 

namespace MoqRefProblem 
{ 
    //This class is the one we want to have passed by ref. 
    public class FileContext 
    { 
     public int LinesProcessed { get; set; } 
     public decimal AmountProcessed { get; set; } 
    } 

    public interface IRecordParser 
    { 
     //The ref parameter below is what's creating the testing problem. 
     void ParseLine(decimal amount, ref FileContext context); 
    } 

    //This is problematic because we don't have a 
    //seam that allows us to set the FileContext. 
    public class OriginalFileParser 
    { 
     private readonly IRecordParser _recordParser; 

     public OriginalFileParser(IRecordParser recordParser) 
     { 
      _recordParser = recordParser; 
     } 

     public void ParseFile(IEnumerable<decimal> items) 
     { 
      //This is the problem 
      var context = new FileContext(); 
      ParseItems(items, ref context); 
     } 

     private void ParseItems(IEnumerable<decimal> items, ref FileContext context) 
     { 
      foreach (var item in items) 
      { 
       _recordParser.ParseLine(item, ref context); 
      } 
     } 
    } 

    } 

    //This class has had the creation of the FileContext extracted into a virtual 
    //method. 
    public class FileParser 
    { 
     private readonly IRecordParser _recordParser; 

     public FileParser(IRecordParser recordParser) 
     { 
      _recordParser = recordParser; 
     } 

     public void ParseFile(IEnumerable<decimal> items) 
     { 
      //Instead of newing up a context, we'll get it from a virtual method 
      //that we'll override in a test class. 
      var context = GetFileContext(); 
      ParseItems(items, ref context); 
     } 

     //This is our extensibility point 
     protected virtual FileContext GetFileContext() 
     { 
      var context = new FileContext(); 
      return context; 
     } 

     private void ParseItems(IEnumerable<decimal> items, ref FileContext context) 
     { 
      foreach (var item in items) 
      { 
       _recordParser.ParseLine(item, ref context); 
      } 
     } 
    } 

    //Create a test class that inherits from the Class under Test  
    //We will set the FileContext object to the value we want to 
    //use. Then we override the GetContext call in the base class 
    //to return the fileContext object we just set up. 
    public class MakeTestableParser : FileParser 
    { 
     public MakeTestableParser(IRecordParser recordParser) 
      : base(recordParser) 
     { 
     } 

     private FileContext _context; 

     public void SetFileContext(FileContext context) 
     { 
      _context = context; 
     } 

     protected override FileContext GetFileContext() 
     { 
      if (_context == null) 
      { 
       throw new Exception("You must set the context before it can be used."); 
      } 

      return _context; 
     } 
    } 

[TestFixture] 
public class WorkingFileParserTest 
{ 
    [Test] 
    public void ThisWillWork() 
    { 
     //Arrange 
     var recordParser = new Mock<IRecordParser>(); 

     //Note that we are an instance of the TestableParser and not the original one. 
     var sut = new MakeTestableParser(recordParser.Object); 
     var context = new FileContext(); 
     sut.SetFileContext(context); 

     var items = new List<decimal>() 
      { 
       10.00m, 
       11.50m, 
       12.25m, 
       14.00m 
      }; 

     //Act 
     sut.ParseFile(items); 

     //Assert 
     recordParser.Verify(x => x.ParseLine(It.IsAny<decimal>(), ref context), Times.Exactly(items.Count)); 
    } 
}