Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
754 views
in Technique[技术] by (71.8m points)

c# - Specifying [readonly] property values [via ctor args] when instantiating [immutable] objects with AutoFixture

My test requires that I set the Response property on an immutable Rsvp object (see below) to a specific value.

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

    public Rsvp(string response)
    {
        Response = response;
    }
}

I initially tried to do this using Build<Rsvp>().With(x => x.Rsvp, "Attending"), but realized this only supports writable properties.

I replaced that with Build<Rsvp>().FromFactory(new Rsvp("Attending")). This works, but is cumbersome for more complex objects where it doesn't matter what some of the properties are.

For instance, if the Rsvp object had a CreatedDate property, this method of instantiating the object would force me to write Build<Rsvp>().FromFactory(new Rsvp("Attending", fixture.Create<DateTime>())).

Is there a way to only specify values for meaning properties for an immutable object?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

Frankly, immutable types are a pain in C#, but you can make it easier to work with a class like Rsvp if you take a cue from F# and introduce copy and update semantics. If you modify Rsvp like this, it's going to be much easier to work with overall, and thus, as a by-product, also to unit test:

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

Notice that I've add two WithXyz methods, that return a new instance with that one value changed, but all other values held constant.

This would enable you to create an instance of Rsvp for testing purposed like this:

var fixture = new Fixture();
var seed = fixture.Create<Rsvp>();
var sut = seed.WithResponse("Attending");

or, as a one-liner:

var sut = new Fixture().Create<Rsvp>().WithResponse("Attending");

If you can't change Rsvp, you can add the WithXyz methods as extension methods.

Once you've done this about a dozen times, you get tired of it, and it's time to make the move to F#, where all that (and more) is built-in:

type Rsvp = {
    Response : string
    CreatedDate : DateTime }

You can create an Rsvp record with AutoFixture like this:

let fixture = Fixture()
let seed = fixture.Create<Rsvp>()
let sut = { seed with Response = "Attending" }

or, as a one-liner:

let sut = { Fixture().Create<Rsvp>() with Response = "Attending" }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...