Open LockTar opened 1 year ago
Hi. Thanks for the question.
Indeed making use of the CreateInstance is the approach when the creation cannot easily be inferred and yes having multiple ThatIsValid variants when your domain has multiple such states also would make sense. To make sure I fully understand, what would the proposed Has give you over using Get?
Ok real scenario because then it's easier to explain.
I have a customer registration. This is a normal process with a status from 1 until 4.
public class CustomerRegistration
{
public string Id { get; init; }
public int Status { get; private set; }
public string Name { get; private set; }
public string Email { get; private set; }
public bool? ConsentGiven { get; private set; }
public CustomerRegistration(string id, string name, string email)
{
Id = id ?? throw new ArgumentNullException(nameof(id));
Name = name ?? throw new ArgumentNullException(nameof(name));
Email = email ?? throw new ArgumentNullException(nameof(email));
Status = 1;
}
public void SetEmailVerifiedStatus()
{
if (Status != 1)
{
throw new Exception();
}
Status = 2;
}
public void SetConsent(bool consent)
{
if (Status != 2)
{
throw new Exception();
}
Status = 3;
ConsentGiven = consent;
}
public void CompleteRegistration()
{
if (Status != 3)
{
throw new Exception();
}
Status = 4;
}
}
Because of the constructor I need to make use of the CreateInstance
. But this will only fill Id
, name
and email
.
When we are in example Status
2 (so after the email is verified) the user needs to consent something.
In order to test this functionality (move from status 2 to 3), I need to be able to set the Status to value 2
.
This is ignored by the builder. I think because of the private set
. Doesn't matter if use the With
or the ThatIsValid
method.
So I need to "set" it in the CreateInstance
. So you get something like this:
internal class CustomerRegistrationBuilder : Builder<CustomerRegistration>
{
private readonly Faker _faker = new();
protected override CustomerRegistration CreateInstance()
{
var registration = new CustomerRegistration(Get(x => x.Id), Get(x => x.Name), Get(x => x.Email));
if (Has(x => x.Status))// So check if a property has even set...
{
int status = Get(x => x.Status);
switch (status)
{
case 2:
registration.SetEmailVerifiedStatus();
break;
case 3:
registration.SetConsent(Get(x => x.ConsentGiven.Value));
break;
case 4:
registration.CompleteRegistration();
break;
}
}
return registration;
}
public CustomerRegistrationBuilder WithStatus(int status)
{
With(x => x.Status, status);
return this;
}
public CustomerRegistrationBuilder ThatIsValidUntilStatusAccountInfoReceived()
{
With(x => x.Id, Guid.NewGuid().ToString());
With(x => x.Status, 1);
With(x => x.Name, _faker.Person.FullName);
With(x => x.Email, _faker.Person.Email);
return this;
}
public CustomerRegistrationBuilder ThatIsValidUntilStatusEmailVerified()
{
With(x => x.Id, Guid.NewGuid().ToString());
With(x => x.Status, 2); // Is ignored here https://github.com/chivandikwa/Ease/issues/2
With(x => x.Name, _faker.Person.FullName);
With(x => x.Email, _faker.Person.Email);
return this;
}
public CustomerRegistrationBuilder ThatIsValidWithStatusCompleted()
{
With(x => x.Id, Guid.NewGuid().ToString());
With(x => x.Status, 4); // Is ignored here https://github.com/chivandikwa/Ease/issues/2
With(x => x.Name, _faker.Person.FullName);
With(x => x.Email, _faker.Person.Email);
With(x => x.ConsentGiven, true); // Is ignored here https://github.com/chivandikwa/Ease/issues/2
return this;
}
public override CustomerRegistrationBuilder ThatIsValid()
{
return ThatIsValidUntilStatusAccountInfoReceived();
}
}
Of course this sample is simplified.
But I don't see another way to set properties like ConsentGiven
only when a particular status is set...
Hopefully you understand the issue.
Hi,
Do you have an idea on how to set properties with a private set after construction?
For simplicity I've created a Customer object.
The Builder. Unable to set the
Age
propertyWhat does work is the following:
But let's say I have strange business logic. For example the
SetEmail
can only be called whenAge
is 0. So actually not being set yet. I could add all those Setxxxx methods in the CreatInstance method but then they always get values. Then I have to overwrite theThatIsValid
values with in exampleAge
is0
again. Not really ideal...I know it is strange but think of a registration process. I have a
Status
property and I need to set some properties only when the status is a particular value. It is not possible now.Possible solution I think the solution is to introduce a
Has
method in the builder so you can do this: