使用AutoFixture实例化[immutable]对象时指定[readonly]属性值[via ctor args]

Mat*_*cek 8 c# unit-testing immutability autofixture

我的测试要求我将Response不可变Rsvp对象(见下文)的属性设置为特定值.

public class Rsvp
{
    public string Response { get; private set; }

    public Rsvp(string response)
    {
        Response = response;
    }
}
Run Code Online (Sandbox Code Playgroud)

我最初尝试使用Build<Rsvp>().With(x => x.Rsvp, "Attending"),但意识到这只支持可写属性.

我替换了Build<Rsvp>().FromFactory(new Rsvp("Attending")).这可行,但对于更复杂的对象来说很麻烦,因为它们与某些属性无关.

例如,如果Rsvp对象具有CreatedDate属性,则实例化对象的这种方法将迫使我编写Build<Rsvp>().FromFactory(new Rsvp("Attending", fixture.Create<DateTime>())).

有没有办法只为不可变对象指定含义属性的值?

Mar*_*ann 9

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

坦率地说,不可变类型是C#中的一个难点,但是Rsvp如果从F#中获取提示并引入复制和更新语义,则可以更容易地使用类.如果你这样修改Rsvp,整体工作会更容易,因此,作为副产品,也可以用于单元测试:

public class Rsvp
{
    public string Response { get; private set; }

    public DateTime CreatedDate { get; private set; }

    public Rsvp(string response, DateTime createdDate)
    {
        Response = response;
        CreatedDate = createdDate;
    }

    public Rsvp WithResponse(string newResponse)
    {
        return new Rsvp(newResponse, this.CreatedDate);
    }

    public Rsvp WithCreatedDate(DateTime newCreatedDate)
    {
        return new Rsvp(this.Response, newCreatedDate);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我添加了两个WithXyz方法,这些方法返回一个新实例,其中一个值已更改,但所有其他值保持不变.

这将使您能够创建一个Rsvp用于测试的实例,如下所示:

var fixture = new Fixture();
var seed = fixture.Create<Rsvp>();
var sut = seed.WithResponse("Attending");
Run Code Online (Sandbox Code Playgroud)

或者,作为一个班轮:

var sut = new Fixture().Create<Rsvp>().WithResponse("Attending");
Run Code Online (Sandbox Code Playgroud)

如果无法更改Rsvp,可以将WithXyz方法添加为扩展方法.

一旦你完成了大约十几次,你就厌倦了它,现在是时候转向F#,其中所有这些(以及更多)是内置的:

type Rsvp = {
    Response : string
    CreatedDate : DateTime }
Run Code Online (Sandbox Code Playgroud)

您可以Rsvp使用AutoFixture 创建一条记录,如下所示:

let fixture = Fixture()
let seed = fixture.Create<Rsvp>()
let sut = { seed with Response = "Attending" }
Run Code Online (Sandbox Code Playgroud)

或者,作为一个班轮:

let sut = { Fixture().Create<Rsvp>() with Response = "Attending" }
Run Code Online (Sandbox Code Playgroud)

  • 这个答案的第一段写得很好,我们实际上可以将它包含在AutoFixture的README文件的描述中. (4认同)
  • +1如果@mattslav可以修改`Rsvp`类(或添加扩展方法),那么这应该是接受的答案. (2认同)