【问题标题】:How can I share state between Bogus (C# Faker) rules?如何在 Bogus (C# Faker) 规则之间共享状态?
【发布时间】:2021-04-29 15:51:01
【问题描述】:

我正在使用Bogus 生成测试数据,但我有一些字段依赖于另一个对象的部分(我希望为每一代随机选择)但必须彼此保持一致。

这可能不是最好的解释,所以希望这个例子能更好地解释它。

我有一个Order,其中包括来自Customer 的ID 和货币。

public class Customer
{
    public Guid Id { get; set; }
    public string Currency { get; set; }
}

public class Order
{
    public Guid Id { get; set; }
    public Guid CustomerId { get; set; } // The ID of a customer
    public string Currency { get; set; } // The currency for the customer identified by CustomerId
    public decimal Amount { get; set; }
}

我可以使用以下方法产生一些客户:

var customerFaker = 
    new Faker<Customer>()
    .StrictMode(true)
    .RuleFor("Id", f => f.Random.Guid())
    .RuleFor("Id", f => f.Finance.Currency());

var customers = customerFaker.Generate(10);

但是当涉及到“共享”在订单生成器的规则之间选择的客户时,我遇到了困难:

var orderFaker =
    new Faker<Order>()
    .StrictMode(true)
    .RuleFor("Id", f => f.Random.Guid())
    .RuleFor("Amount", f => f.Finance.Amount())
    .RuleFor("CustomerId", f => f.PickRandom(customers).Id)
    //How do I share the Customer that's been chosen to use it in the rule below?
    .RuleFor("Currency", f => f.PickRandom(customers).Currency);

我想出了一些不太理想的方法(比如每次都实例化一个新的 Faker 并传入一个随机客户),但我正在处理非常复杂的对象和依赖项,所以我会如果可能的话,希望避免这种情况。

我目前的想法是,最好的方法可能是扩展 Order 类以便能够存储 Customer,然后再将其转换为订单。考虑到我需要为其执行此操作的模型数量,如果可能的话,我想避免这种情况。

public class OrderWithCustomer : Order
{
    public Customer Customer { get; set; }
}

var orderWithCustomerFaker =
    new Faker<OrderWithCustomer>()
    .StrictMode(true)
    .RuleFor("Id", f => f.Random.Guid())
    .RuleFor("Amount", f => f.Finance.Amount())
    .RuleFor("Customer", f => f.PickRandom(customers))
    .RuleFor("CustomerId", (f, o) => o.Customer.Id)
    .RuleFor("Currency", (f, o) => o.Customer.Currency);

var orders = 
    orderWithCustomerFaker
    .Generate(10)
    .Select(withCustomer => (Order)withCustomer);

【问题讨论】:

  • 还有其他方法,它与您所描述的类似,但在Order 类中,您只能引用CustomerCustomerIdCurrency 将被省略。稍后,当您需要货币或 CustID 时,您只需通过 someOrder.Customer.IdsomeOrder.Customer.Currency 之类的引用访问它们。
  • 不幸的是,这是不可能的,因为我正在使用一组我需要遵守的固定模型。

标签: c# testing test-data bogus


【解决方案1】:

虽然 Bogus 非常适合生成所有随机数据,但当您需要基于现有关系进行链接时,您不想再次获得随机数据。相反,在这个后期步骤中,您希望根据之前的分配选择现有数据

首先,您将所有customers 生成为List&lt;Customer&gt;

var customerFaker = new Faker<Customer>()
    .StrictMode(true)
    .RuleFor("Id", f => f.Random.Guid())
    .RuleFor("Id", f => f.Finance.Currency());

var customers = customerFaker.Generate(10);

您可以直接访问该列表并在所需值内匹配/查找(通过自定义方法或使用 Linq):

using System.Linq; //for linq method, add at the top of file

var orderFaker = new Faker<Order>()
    .StrictMode(true)
    .RuleFor("Id", f => f.Random.Guid())
    .RuleFor("Amount", f => f.Finance.Amount())
    .RuleFor("CustomerId", f => f.PickRandom(customers).Id)

    //Assuming that you implement your GetById() method or something similar
    .RuleFor("Currency", (f,o) => customers.GetById(o.CustomerId).Currency);

    //Or directly by using linq
    .RuleFor("Currency", (f,o) => customers.First(c => c.Id == o.CustomerId).Currency);

这一切都应该起作用,因为RuleFor() 中的第二个参数是setter,它将值分配给属性。你甚至可以这样做:

var orderFaker = new Faker<Order>()
    .RuleFor("Currency", f => "EUR");

【讨论】:

  • 我希望避免这样做,因为我实际上没有客户列表,我正在注入一个 API 数据提供程序,我希望避免为每一代调用(主要是因为这意味着我需要编写一堆代码来缓存请求)。我想我希望能够做类似Faker.SetExtra("customer", Faker.PickRandom(customers)) 之类的事情,然后在一个setter lambdas 中使用Faker.GetExtra&lt;Customer&gt;("customer")
  • @John 在这种情况下,您仍然可以回退到以前的解决方案(我在问题下的 cmets 中提到了它)。这也会使工作顺利进行。您可以添加一些可以处理的属性(setters/getters)。例如。如果你分配Customer,在后面会分配 CustomerId 和 Currency 属性。
猜你喜欢
  • 2016-11-02
  • 2010-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-04
  • 2019-01-31
相关资源
最近更新 更多