Autofixture 生成自定义列表 [英] Autofixture generate custom list

查看:51
本文介绍了Autofixture 生成自定义列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的对象:

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;
    }
}

我需要一个填充了 Quote 列表的 Symbol 实例.

I need an instance of Symbol populated with a list of Quote.

在我的测试中,我想验证我是否可以返回收盘价低于或高于特定值的所有报价.这是我的测试:

An in my test, I want to verify that I can return all quotes which the close price is under or above a specific value. Here's my test:

[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));
}

好的.

现在,我想使用 Autofixture 来做这件事,我是一个真正的初学者.我已经在互联网上阅读了有关此工具的几乎所有内容(作者的博客、codeplex 常见问题解答、github 源代码).我了解 autofixture 的基本功能,但现在我想在我的实际项目中使用 autofixture.这是我到目前为止尝试过的.

Now, I wanna do it using Autofixture, im a real beginner. I've read pretty much everything I could on internet about this tool (the author's blog, codeplex FAQ, github source code). I understand the basic features of autofixture, but now I wanna use autofixture in my real project. Here's what I tried so far.

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));

理想情况下,我更喜欢直接创建 Symbol 的夹具,但我不知道如何自定义我的报价列表.而且我不确定我的测试是否通用,因为在另一个测试中,我需要检查特定值是否高于而不是低于,因此我将复制夹具代码"并手动添加引号 =51.

Ideally, I would prefer to directly create a fixture of Symbol, but I don't know how to customize my list of quotes. And I'm not sure that my test is generic, because, in another test, I will need to check that a specific value is above instead of under, so I'm gonna duplicate the "fixture code" and manual add a quote = 51.

所以我的问题是:

1 - 这是我应该如何使用 autofixture 的方式吗?

1 - Is it the way how I'm supposed to use autofixture?

2 - 是否可以改进我在示例中使用 autofixture 的方式?

2 - Is it possible to improve the way I use autofixture in my example ?

推荐答案

AutoFixture 最初是作为测试驱动开发 (TDD) 的工具而构建的,而 TDD 就是关于反馈.本着 GOOS 的精神,您应该聆听您的测试.如果测试很难编写,您应该考虑您的 API 设计.AutoFixture 倾向于放大这种反馈,这就是它告诉我的.

AutoFixture was originally build as a tool for Test-Driven Development (TDD), and TDD is all about feedback. In the spirit of GOOS, you should listen to your tests. If the tests are hard to write, you should consider your API design. AutoFixture tends to amplify that sort of feedback, and here's what it's telling me.

让比较更容易

首先,虽然与 AutoFixture 无关,Quote 类只是请求变成一个合适的值对象,所以我会覆盖 Equals 以更轻松地比较预期和实际实例:

First, while not related to AutoFixture, the Quote class just begs to be turned into a proper Value Object, so I'll override Equals to make it easier to compare expected and actual instances:

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;
}

(确保也覆盖 GetHashCode.)

复制和更新

上面的测试尝试似乎意味着我们缺乏一种方法来改变单个字段同时保持其余字段不变.从函数式语言中得到启发,我们可以在 Quote 类本身上引入一种方法来做到这一点:

The above attempt at a test seems to imply that we're lacking a way to vary a single field while keeping the rest constant. Taking a cue from functional languages, we can introduce a way to do that on the Quote class itself:

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

这种 API 在值对象上往往非常有用,以至于我总是将此类方法添加到我的值对象中.

This sort of API tends to be very useful on Value Objects, to the point where I always add such methods to my Value Objects.

让我们对 Symbol 做同样的事情:

Let's do the same with Symbol:

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

这样可以更轻松地让 AutoFixture 处理您不关心的所有事情,同时明确说明您关心的事情.

This makes it much easier to ask AutoFixture to deal with all the stuff you don't care about while explicitly stating only that which you care about.

使用 AutoFixture 进行测试

原始测试现在可以重写为:

The original test can now be rewritten as:

[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);
}

此测试仅说明您关心的测试用例的那些部分,而 AutoFixture 负责处理对测试用例没有任何影响的所有其他值.这使得测试更健壮,也更具可读性.

This test only states those parts of the test case that you care about, while AutoFixture takes care of all the other values that don't have any impact on the test case. This makes the test more robust, as well as more readable.

这篇关于Autofixture 生成自定义列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆