2014-10-20 117 views
2

我正在實施一個存儲庫模式查詢類和使用NSubstitute測試。NSubstitute不匹配Linq表達式

庫接口:

public interface IMyRepository 
{ 
    IQueryable<T> Query<T>(Expression<Func<T, bool>> filter) where T : class; 
} 

DateTimeProvider接口:

public interface IMyDateTimeProvider 
{ 
    DateTime GetDateNow(); 
} 

應用接口:

public interface IMyApplication 
{ 
    List<Thing> GetThingsByQuery(int status); 
} 

應用實現:

public class MyApplication : IMyApplication 
{ 
    private readonly IMyRepository myRepository; 

    private readonly IMyDateTimeProvider myDateTimeProvider; 

    public MyApplication(IMyRepository myRepository, IMyDateTimeProvider myDateTimeProvider) 
    { 
     this.myRepository = myRepository; 
     this.myDateTimeProvider = myDateTimeProvider; 
    } 

    public List<Thing> GetThingsByQuery(int status) 
    { 
     var createdDate = this.myDateTimeProvider.GetDateNow(); 

     return this.myRepository.Query<Thing>(t => t.CreatedDate == createdDate && t.Status == status).ToList(); 
    } 
} 

測試:

[TestClass] 
public class ApplicationTest 
{ 
    private IMyApplication myApplication; 

    private IMyDateTimeProvider myDateTimeProvider; 

    private IMyRepository myRepository; 

    [TestMethod] 
    public void QueriesRepository() 
    { 
     // Arrange 
     var createdDate = new DateTime(2014, 1, 1); 

     this.myDateTimeProvider.GetDateNow().Returns(createdDate); 

     const int Status = 1; 

     // Act 
     this.myApplication.GetThingsByQuery(Status); 

     // Assert 
     this.myRepository.Received().Query<Thing>(t => t.CreatedDate == createdDate && t.Status == Status); 
    } 

    [TestInitialize] 
    public void TestInitialize() 
    { 
     this.myRepository = Substitute.For<IMyRepository>(); 

     this.myDateTimeProvider = Substitute.For<IMyDateTimeProvider>(); 

     this.myApplication = new MyApplication(this.myRepository, this.myDateTimeProvider); 
    } 
} 

但測試失敗,出現以下消息:

NSubstitute.Exceptions.ReceivedCallsException: Expected to receive a call matching: 
    Query<Thing>(t => ((t.CreatedDate == value(MySolution.Test.ApplicationTest+<>c__DisplayClass0).createdDate) AndAlso (t.Status == 1))) 
Actually received no matching calls. 
Received 1 non-matching call (non-matching arguments indicated with '*' characters): 
    Query<Thing>(*t => ((t.CreatedDate == value(MySolution.Application.MyApplication+<>c__DisplayClass0).createdDate) AndAlso (t.Status == value(MySolution.Application.MyApplication+<>c__DisplayClass0).status))*) 

的日期時間和狀態被解析成value()它們是應用與試驗之間不同。

這是爲什麼?我怎樣才能解決這個問題?

回答

0

默認的相等比較用於表達正被使用(參照平等):

例如,在表達式(t => t.CreatedDate == createdDate && t.Status == Status``)

this.myRepository.Received().Query<Thing>(t => t.CreatedDate == createdDate 
              && t.Status == Status   ); 

是一個不同的實例中的表達:

return this.myRepository.Query<Thing>(t => t.CreatedDate == createdDate 
             && t.Status == status   ).ToList(); 

要修復驗證此方法調用檢出argument matchers within NSubstitute

但是作爲一個例子:

Func<Expression<Thing, bool>, bool> validator = 
    // TODO this needs to be written properly, based on the expression, 
    // not its string representation 
    e => e.Body.ToString() == "t.CreatedDate == createdDate 
     && t.Status == Status"; 
this.myRepository.Received().Query<Thing>(Arg.Is<Expression<Thing, bool>>(validator)); 
+0

我不知道如何去實現。我試過'this.myRepository.Received()。查詢(t => t.CreatedDate == Arg.Any ()&& t.Status == Arg.Any ());''''和'this.myRepository。收到()。查詢(t => t.CreatedDate == Arg.Is(createdDate)&& t.Status == Arg.Is(Status));'但是兩個仍然使用'value()'並且失敗 – Shevek 2014-10-20 13:59:55

+0

@ Shevek回答更新 – 2014-10-20 14:03:43

+0

我無法讓你的例子編譯。這編譯但測試失敗,具有完全相同的錯誤:'Expression >驗證程序= t => t.CreatedDate == createdDate && t.Status ==狀態;'和'this.myRepository.Received()。查詢 (Arg.Is >>(validator));' – Shevek 2014-10-20 15:24:46

1

對於複雜的表達式,如果經常發現它更容易上捕獲參數通過callbacksReceived()斷言。一個(不完整的)示例:

Expression<Func<Thing, bool>> receivedFilter receivedFilter = null; 
myRepository.When(x => x.Query<Thing>(Arg.Any<...>)) 
    .Do(x => receivedQuery = x.Arg<Expression<Func<Thing, bool>>>()); 

然後,對捕獲的過濾器表達式進行斷言。它實際上可能簡單到只需要執行表達的過濾器FUNC(見例如here

Func<Thing, bool> predicate = receivedFilter.Compile(); 
var matchingThing = new Thing 
    { CreatedDate = createdData, Status = Status }; 
// assert matching 
predicate(matchingThing).Should().BeTrue(); 

// assert non.matching 
predicate(nonMatchingThing).Should().BeFalse(); 

這種做法似乎讓測試多一點黑盒裝但這一般不是一件壞事。