2012-04-12 8 views
10

Tôi đang cố gắng thực hiện một số bằng chứng cơ bản về mã loại khái niệm cho dự án mvc3 mới. Chúng tôi đang sử dụng Moq với RavenDB.Mock IRavenQueryable với biểu thức Where() nối thêm

Hành động:

public ActionResult Index(string id) 
{ 
    var model = DocumentSession.Query<FinancialTransaction>() 
     .Where(f => f.ResponsibleBusinessId == id); 
    return View(model); 
} 

Test:

private readonly Fixture _fixture = new Fixture(); 

[Test] 
public void Index_Action_Returns_List_Of_FinancialTransactions_For_Business([Random(0, 50, 5)]int numberOfTransactionsToCreate) 
{ 
    // Arrange 
    var session = new Mock<IDocumentSession>(); 
    var financialController = new FinancialController { DocumentSession = session.Object }; 

    var businessId = _fixture.CreateAnonymous<string>(); 
    var transactions = _fixture.Build<FinancialTransaction>() 
     .With(f => f.ResponsibleBusinessId, businessId) 
     .CreateMany(numberOfTransactionsToCreate); 

    // Mock 
    var ravenQueryableMock = new Mock<IRavenQueryable<FinancialTransaction>>(); 
    ravenQueryableMock.Setup(x => x.GetEnumerator()).Returns(transactions.GetEnumerator); 
    ravenQueryableMock.Setup(x => x.Customize(It.IsAny<Action<Object>>()).GetEnumerator()).Returns(() => transactions.GetEnumerator()); 

    session.Setup(s => s.Query<FinancialTransaction>()).Returns(ravenQueryableMock.Object).Verifiable(); 

    // Act 
    var actual = financialController.Index(businessId) as ViewResult; 

    // Assert 
    Assert.IsNotNull(actual); 
    Assert.That(actual.Model, Is.InstanceOf<List<FinancialTransaction>>()); 

    var result = actual.Model as List<FinancialTransaction>; 
    Assert.That(result.Count, Is.EqualTo(numberOfTransactionsToCreate)); 
    session.VerifyAll(); 
} 

Nó sẽ xuất hiện vấn đề là trong .Where (f => f.ResponsibleBusinessId == id). Từ IRavenQueryable được chế giễu, tôi sẽ trả về danh sách các TransactionTransactions, vì vậy người ta sẽ nghĩ .Where() sẽ lọc dựa trên đó. Nhưng vì nó là IQueryable, tôi đoán nó đang cố gắng thực hiện biểu thức tất cả là một, khi nó liệt kê.

Để xác minh, tôi đã thay đổi truy vấn của người hành động như thế này:

var model = DocumentSession.Query<FinancialTransaction>() 
    .ToList() 
    .Where(f => f.ResponsibleBusinessId == id); 

này để cho các đường chuyền kiểm tra, tuy nhiên, nó không phải là lý tưởng, vì điều đó có nghĩa là nó sẽ liệt kê tất cả các hồ sơ, sau đó lọc chúng.

Có cách nào để Moq hoạt động với điều này không?

+2

Là một ý tưởng phụ, thay vì chế nhạo RavenDB, bạn có nghĩ đến việc sử dụng phiên bản Nhúng trong bộ nhớ không? Mocking với phương pháp mở rộng LINQ là (thực sự) khủng khiếp để đối phó với, và tôi đã đi với RavenDB vì không có nhu cầu thực sự để thử ra cơ sở dữ liệu. – Rangoric

+0

Tôi đang sử dụng @Rangoric - không cần phải giả lập công cụ 'IDocumentSession' và' IDocumentStore' (hoặc chế giễu bất kỳ Ravendb) nào khi RavenDb có một 'EmbeddedDocumentStore'. Dude, nhảy vào http://jabbr.net/#/rooms/RavenDB và tất cả chúng ta sẽ trò chuyện với bạn tại sao/tại sao không. –

+2

Điều đó là tốt - ngoại trừ việc tôi thấy RavenDB mất quá nhiều thời gian để bắt đầu kiểm tra đơn vị của tôi - nói rằng "Nó được thiết kế không bị chế giễu" không phải là câu trả lời đúng. – Ronnie

Trả lời

7

Như đã đề cập trong các nhận xét, bạn không nên mô phỏng RavenDB API trong các bài kiểm tra của mình.

RavenDB đã hỗ trợ tuyệt vời cho kiểm tra đơn vị, nhờ vào chế độ InMemory:

[Test] 
public void MyTest() 
{ 
    using (var documentStore = new EmbeddableDocumentStore { RunInMemory = true }) 
    { 
     documentStore.Initialize(); 

     using (var session = documentStore.OpenSession()) 
     { 
      // test 
     } 
    } 
} 
+6

Mất gần 3 giây trên máy của tôi để tải và khởi chạy máy chủ nhúng Raven. Đây là khoảng thời gian không thể chấp nhận để thêm vào các bài kiểm tra đơn vị. –

+0

@MikeScott 3 giây không đúng. Thông số kỹ thuật của máy của bạn là gì? –

+0

Mất bao lâu? Bạn đã xem lại nó trong một bài kiểm tra đơn vị, bao gồm cả việc chạy các tác vụ tạo chỉ mục? Win7/64 trên Intel Core 2 Duo E4600 với tốc độ 2,4 GHz, RAM 4GB. –

3

Như những người khác đã đề cập, nếu bạn có thể nhận được bằng với chế độ trong bộ nhớ/nhúng nó là rất tốt cho hội nhập thử nghiệm . Nhưng nó không phải là nhanh chóng hoặc dễ dàng, đủ để thử nghiệm đơn vị, theo ý kiến ​​của tôi.

Tôi tìm thấy một blog post bởi Sam Ritchie cung cấp một 'giả' (wrapper xung quanh một tiêu chuẩn LINQ IQueryable) cho một IRavenQueryable cho các trường hợp như thế này. Anh ấy hơi lỗi thời, vì các phiên bản mới hơn của Raven (hiện tại 2.5) cung cấp một vài phương thức bổ sung trên giao diện IRavenQueryable. Hiện tại, tôi không sử dụng các phương pháp mới đó (TransformWith, AddQueryInput, Spatial), vì vậy, tôi chỉ để lại một cách mờ nhạt NotImplementedException trong mã bên dưới.

Xem Sam's post cho mã ban đầu tôi dựa trên mã này và các ví dụ về cách sử dụng.

public class FakeRavenQueryable<T> : IRavenQueryable<T> { 
    private readonly IQueryable<T> source; 

    public FakeRavenQueryable(IQueryable<T> source, RavenQueryStatistics stats = null) { 
     this.source = source; 
     this.QueryStatistics = stats; 
    } 

    public RavenQueryStatistics QueryStatistics { get; set; } 

    public Type ElementType { 
     get { return typeof(T); } 
    } 

    public Expression Expression { 
     get { return this.source.Expression; } 
    } 

    public IQueryProvider Provider { 
     get { return new FakeRavenQueryProvider(this.source, this.QueryStatistics); } 
    } 

    public IRavenQueryable<T> Customize(Action<IDocumentQueryCustomization> action) { 
     return this; 
    } 

    public IRavenQueryable<TResult> TransformWith<TTransformer, TResult>() where TTransformer : AbstractTransformerCreationTask, new() { 
     throw new NotImplementedException(); 
    } 

    public IRavenQueryable<T> AddQueryInput(string name, RavenJToken value) { 
     throw new NotImplementedException(); 
    } 

    public IRavenQueryable<T> Spatial(Expression<Func<T, object>> path, Func<SpatialCriteriaFactory, SpatialCriteria> clause) { 
     throw new NotImplementedException(); 
    } 

    public IRavenQueryable<T> Statistics(out RavenQueryStatistics stats) { 
     stats = this.QueryStatistics; 
     return this; 
    } 

    public IEnumerator<T> GetEnumerator() { 
     return this.source.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() { 
     return this.source.GetEnumerator(); 
    } 
} 

public class FakeRavenQueryProvider : IQueryProvider { 
    private readonly IQueryable source; 

    private readonly RavenQueryStatistics stats; 

    public FakeRavenQueryProvider(IQueryable source, RavenQueryStatistics stats = null) { 
     this.source = source; 
     this.stats = stats; 
    } 

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { 
     return new FakeRavenQueryable<TElement>(this.source.Provider.CreateQuery<TElement>(expression), this.stats); 
    } 

    public IQueryable CreateQuery(Expression expression) { 
     var type = typeof(FakeRavenQueryable<>).MakeGenericType(expression.Type); 
     return (IQueryable)Activator.CreateInstance(type, this.source.Provider.CreateQuery(expression), this.stats); 
    } 

    public TResult Execute<TResult>(Expression expression) { 
     return this.source.Provider.Execute<TResult>(expression); 
    } 

    public object Execute(Expression expression) { 
     return this.source.Provider.Execute(expression); 
    } 
} 
+0

Các thử nghiệm đơn vị thực sự là kiểm thử tích hợp là một vấn đề thực sự. Các bài kiểm thử đơn vị nên chạy trong một thời gian ngắn nhất có thể để có thể sử dụng được - và được sử dụng - trong một thời gian dài. – utunga

+0

@utunga Tôi không chắc bạn đang nói gì. Nếu bạn muốn bắt đầu một cuộc thảo luận về các bài kiểm tra đơn vị so với các bài kiểm tra tích hợp, điều đó có thể là trong trò chuyện hoặc trong một câu hỏi tại programmers.stackexchange.com. Nếu đó không phải là những gì bạn đang nói về, và có một cái gì đó để làm với câu trả lời này cụ thể, xin vui lòng làm rõ. –

+2

xin lỗi ... những gì tôi thực sự có nghĩa là để nói là "câu trả lời này nên được ở đầu bởi vì các bài kiểm tra đơn vị thực sự nên được thiết kế để chạy trong một thời gian ngắn ..". Không nhận được toàn bộ bài kiểm tra đơn vị và thử nghiệm tích hợp (và lưu ý rằng Ayende dường như đã chuyển vị trí của mình sang các bài kiểm tra tích hợp và tránh các bài kiểm tra đơn vị cộng với mocks hoặc hàng giả) nhưng cách tôi nhìn vào nó, trong khi tôi đồng ý với mọi người làm bất cứ điều gì họ muốn trong vấn đề này, câu hỏi đã được hỏi về Moq do đó người đó là sau một (đúng) phương pháp thử nghiệm đơn vị - vì vậy câu trả lời của bạn nên được ưa thích (mặc dù nó là giả không phải là một giả). – utunga