2013-07-24 37 views
5

Dưới đây là đối tượng của tôi:Autofixture tạo danh sách tùy chỉnh

public class Symbol 
{ 
    private readonly string _identifier; 
    private readonly IList<Quote> _historicalQuotes; 

    public Symbol(string identifier, IEnumerable<Quote> historicalQuotes = null) 
    { 
     _identifier = identifier; 
     _historicalQuotes = historicalQuotes; 
    } 
} 

public class Quote 
{ 
    private readonly DateTime _tradingDate; 
    private readonly decimal _open; 
    private readonly decimal _high; 
    private readonly decimal _low; 
    private readonly decimal _close; 
    private readonly decimal _closeAdjusted; 
    private readonly long _volume; 

    public Quote(
     DateTime tradingDate, 
     decimal open, 
     decimal high, 
     decimal low, 
     decimal close, 
     decimal closeAdjusted, 
     long volume) 
    { 
     _tradingDate = tradingDate; 
     _open = open; 
     _high = high; 
     _low = low; 
     _close = close; 
     _closeAdjusted = closeAdjusted; 
     _volume = volume; 
    } 
} 

Tôi cần một thể hiện của Symbol dân cư với một danh sách các Quote.

Trong thử nghiệm của mình, tôi muốn xác minh rằng tôi có thể trả lại tất cả các trích dẫn có giá đóng thấp hơn hoặc cao hơn giá trị cụ thể. Đây là thử nghiệm của tôi:

[Fact] 
public void PriceUnder50() 
{ 
    var msftIdentifier = "MSFT"; 
    var quotes = new List<Quote> 
    { 
     new Quote(DateTime.Parse("01-01-2009"), 0, 0, 0, 49, 0, 0), 
     new Quote(DateTime.Parse("01-02-2009"), 0, 0, 0, 51, 0, 0), 
     new Quote(DateTime.Parse("01-03-2009"), 0, 0, 0, 50, 0, 0), 
     new Quote(DateTime.Parse("01-04-2009"), 0, 0, 0, 10, 0, 0) 
    }; 

    _symbol = new Symbol(msftIdentifier, quotes); 


    var indicator = new UnderPriceIndicator(50); 
    var actual = indicator.Apply(_symbol); 

    Assert.Equal(2, actual.Count); 
    Assert.True(actual.Any(a => a.Date == DateTime.Parse("01-01-2009"))); 
    Assert.True(actual.Any(a => a.Date == DateTime.Parse("01-04-2009"))); 
    Assert.True(actual.Any(a => a.Price == 49)); 
    Assert.True(actual.Any(a => a.Price == 10)); 
} 

OK.

Bây giờ, tôi muốn làm điều đó bằng cách sử dụng Tự động trộn, tôi là người mới bắt đầu thực sự. Tôi đã đọc khá nhiều mọi thứ có thể trên internet về công cụ này (blog của tác giả, câu hỏi thường gặp về codeplex, mã nguồn github). Tôi hiểu các tính năng cơ bản của autofixture, nhưng bây giờ tôi muốn sử dụng autofixture trong dự án thực sự của tôi. Đây là những gì tôi đã cố gắng cho đến nay.

var msftIdentifier = "MSFT"; 

var quotes = new List<Quote>(); 
var random = new Random(); 
fixture.AddManyTo(
    quotes, 
    () => fixture.Build<Quote>().With(a => a.Close, random.Next(1,49)).Create()); 
quotes.Add(fixture.Build<Quote>().With(a => a.Close, 49).Create()); 

_symbol = new Symbol(msftIdentifier, quotes); 

// I would just assert than 49 is in the list 
Assert.True(_symbol.HistoricalQuotes.Contains(new Quote... blabla 49)); 

Lý tưởng nhất, tôi muốn trực tiếp tạo biểu trưng cho biểu tượng, nhưng tôi không biết cách tùy chỉnh danh sách báo giá của mình. Và tôi không chắc rằng thử nghiệm của tôi là chung chung, bởi vì, trong một thử nghiệm khác, tôi sẽ cần kiểm tra giá trị cụ thể ở trên thay vì dưới, vì vậy tôi sẽ lặp lại "mã cố định" và thêm hướng dẫn = 51.

Vì vậy, câu hỏi của tôi là:

1 - Có phải đó là cách tôi phải sử dụng tự động trộn?

2 - Có thể cải thiện cách tôi sử dụng tự động trộn trong ví dụ của mình không?

Trả lời

10

Tự động trộn ban đầu được xây dựng như một công cụ cho Phát triển theo hướng thử nghiệm (TDD) và TDD là tất cả về phản hồi. Theo tinh thần của GOOS, bạn nên nghe thử nghiệm của mình. Nếu các bài kiểm tra khó viết, bạn nên xem xét thiết kế API của mình. AutoFixture có xu hướng khuếch đại loại phản hồi và đây là những gì nó cho tôi biết.

Hãy so sánh dễ dàng hơn

Thứ nhất, trong khi không liên quan đến AutoFixture, lớp Quote chỉ cầu xin để được biến thành một hợp Value Object, vì vậy tôi sẽ ghi đè Equals để làm cho nó dễ dàng hơn để so sánh trường hợp dự kiến ​​và thực tế:

public override bool Equals(object obj) 
{ 
    var other = obj as Quote; 
    if (other == null) 
     return base.Equals(obj); 

    return _tradingDate == other._tradingDate 
     && _open == other._open 
     && _high == other._high 
     && _low == other._low 
     && _close == other._close 
     && _closeAdjusted == other._closeAdjusted 
     && _volume == other._volume; 
} 

(Hãy chắc chắn để ghi đè GetHashCode quá.)

Sao chép và cập nhật

Nỗ lực trên tại một thử nghiệm dường như ngụ ý rằng chúng ta đang thiếu một cách để khác nhau một trường duy nhất trong khi vẫn giữ phần còn lại liên tục. Với sự gợi ý từ các ngôn ngữ chức năng, chúng tôi có thể giới thiệu một cách để làm điều đó trên lớp Quote bản thân:

public Quote WithClose(decimal newClose) 
{ 
    return new Quote(
     _tradingDate, 
     _open, 
     _high, 
     _low, 
     newClose, 
     _closeAdjusted, 
     _volume); 
} 

loại này của API có xu hướng được rất hữu ích về các đối tượng giá trị gia tăng, đến mức tôi luôn luôn thêm các phương pháp như vậy cho các đối tượng giá trị của tôi.

Hãy làm tương tự với Symbol:

public Symbol WithHistoricalQuotes(IEnumerable<Quote> newHistoricalQuotes) 
{ 
    return new Symbol(_identifier, newHistoricalQuotes); 
} 

này làm cho nó dễ dàng hơn để ask AutoFixture để đối phó với tất cả những thứ bạn không quan tâm đến trong khi chỉ có vậy mà bạn quan tâm nêu một cách rõ ràng.

Testing với AutoFixture

Các thử nghiệm ban đầu bây giờ có thể được viết lại như sau:

[Fact] 
public void PriceUnder50() 
{ 
    var fixture = new Fixture(); 
    var quotes = new[] 
    { 
     fixture.Create<Quote>().WithClose(49), 
     fixture.Create<Quote>().WithClose(51), 
     fixture.Create<Quote>().WithClose(50), 
     fixture.Create<Quote>().WithClose(10), 
    }; 
    var symbol = fixture.Create<Symbol>().WithHistoricalQuotes(quotes); 
    var indicator = fixture.Create<UnderPriceIndicator>().WithLimit(50); 

    var actual = indicator.Apply(symbol); 

    var expected = new[] { quotes[0], quotes[3] }; 
    Assert.Equal(expected, actual); 
} 

thử nghiệm này chỉ khẳng định những phần của vụ án thử nghiệm mà bạn quan tâm, trong khi AutoFixture sẽ chăm sóc của tất cả các giá trị khác không có bất kỳ tác động nào đối với trường hợp thử nghiệm. Điều này làm cho thử nghiệm mạnh mẽ hơn, cũng như dễ đọc hơn.

+1

+1 @Gui và những người khác Đánh dấu là quá lịch sự để gắn cờ nó nhưng khóa học [Kiểm tra đơn vị nâng cao đơn vị số nhiều nâng cao] của anh ấy (http://pluralsight.com/training/courses/TableOfContents?courseName=advanced-unit-testing) là chock đầy đủ các loại tốt đẹp. Giá trị trả tiền cho nhưng thậm chí tốt hơn họ có một thử nghiệm miễn phí và tôi không thể nghĩ ra một khóa học tốt hơn để sử dụng nó trên (mặc dù tất nhiên [Bên ngoài trong thử nghiệm phát triển Driven] (http://pluralsight.com/training/Courses/TableOfContents/ bên ngoài-trong-tdd) là một thứ hai gần) –

+4

@Mark Seemann Cảm ơn bạn đã bình luận của bạn Mark. Tôi đồng ý thử nghiệm của bạn chắc chắn là dễ đọc hơn. Tôi đã lên kế hoạch để hỏi thêm một số câu hỏi, nhưng câu trả lời của bạn đã hoàn chỉnh, nó luôn trả lời từng câu hỏi của tôi mỗi khi tôi đọc lại câu trả lời của bạn. Omg, bạn có lẽ là thần thử nghiệm đơn vị. – Gui

+0

Làm thế nào về việc thêm các phương thức mở rộng cho 'WithClose',' WithLimit' * được định nghĩa trong dự án thử nghiệm *, thay vì thêm chúng như là các phương thức ví dụ? (bây giờ trong C# 6, họ thậm chí không cần phải ở trong các lớp tĩnh) –

Các vấn đề liên quan