Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alllow Generate JsonNode/Proxy of Faker<T> #458

Open
lucasteles opened this issue Jan 13, 2023 · 1 comment
Open

Alllow Generate JsonNode/Proxy of Faker<T> #458

lucasteles opened this issue Jan 13, 2023 · 1 comment

Comments

@lucasteles
Copy link

The typed Faker class is great to create complex builder scenarios, the power of defining the shape of the type in parts as necessary is one of the best things to solve problems like test scenarios.

Something like this:

public class User
{
    public string Name { get; init; }
    public int Age { get; init; }
    public string Email { get; init; }
}

var builder = new Faker<User>()
    .RuleFor(x => x.Email, f => f.Person.Email)
    .RuleFor(x => x.Age, f => f.Random.Int(18, 50))
    .RuleFor(x => x.Name, f => f.Person.FirstName);

Things get a bit complicated when the Type has a complex constructor or a mix with required properties:

var builder = new Faker<User>()
   .RuleFor(x => x.Age, f => f.Random.Int(18, 50))
   .RuleFor(x => x.Name, f => f.Person.FirstName)
   .CustomInstantiator(f =>
       new User(f.Person.Email)
       {
           Name = // need to be set //,
           Age = // need to be set //,
       });

In this case, we kinda lost the power of member composability. If it was a custom builder I would have to create fields to hold values for the CustomInstantiator etc.

So would be nice to have a way to get the shape of the data without constructing the actual Type

A generic way to solve this could be to have a new method like GetJsonObject, which for the case above could be something like:

var builder = new Faker<User>()
    .RuleFor(x => x.Email, f => f.Person.Email)
    .RuleFor(x => x.Age, f => f.Random.Int(18, 50))
    .RuleFor(x => x.Name, f => f.Person.FirstName);

var values = builde.GenerateJsonNode();
/*
new JsonObject
    {
        ["Name"] = "Casey",
        ["Age"] = 22,
        ["Email"] = "Casey_Ebert22@hotmail.com",
    };
*/

So it could be used on the complex constructor, preserving the member composability:

var builder = new Faker<User>()
   .RuleFor(x => x.Email, f => f.Person.Email)
   .RuleFor(x => x.Age, f => f.Random.Int(18, 50))
   .RuleFor(x => x.Name, f => f.Person.FirstName)
   .CustomInstantiator(f => {
      var values = f.GetJsonObject();
      return new User((values["Email"].GetValue<string>())
       {
           Name = values["Name"].GetValue<string>(),
           Age = values["Age"].GetValue<int>(),
       });
  }

Another solution would be use Castle DynamicProxy to maintain the type contract but mocking the property values:

var builder = new Faker<User>()
   .RuleFor(x => x.Email, f => f.Person.Email)
   .RuleFor(x => x.Age, f => f.Random.Int(18, 50))
   .RuleFor(x => x.Name, f => f.Person.FirstName)
   .CustomInstantiator(f => {
      var proxy= f.GetObjectProxy();
      return new User(proxy.Email)
       {
           Name = proxy.Name,
           Age = proxy.Age
       });
  }
  • Is the feature something that currently cannot be done?
    I don't think so

  • What alternatives have you considered?
    Customize the Faker inheriting from it, or just create/initialize all the properties manually

  • Is this feature request any issues or current problems? No

  • Has the feature been requested in the past? No

If the feature request is approved, would you be willing to submit a PR?
Yes

@bchavez
Copy link
Owner

bchavez commented Jan 28, 2023

Hi @lucasteles; could you give me a few concrete examples of what you're trying to accomplish? Full examples that can compile would help a lot.

I'm having some trouble understanding your request. The following seems to work fine:

void Main()
{
   var builder = new Faker<User>()
       .RuleFor(x => x.Email, f => f.Person.Email)
       .RuleFor(x => x.Age, f => f.Random.Int(18, 50))
       .RuleFor(x => x.Name, f => f.Person.FirstName);
       
   builder.Generate().Dump();
}

public class User
{
   public string Name { get; init; }
   public int Age { get; init; }
   public string Email { get; init; }
}

image

If you're finding that the following is too complex or unsightly:

var builder = new Faker<User>()
   .RuleFor(x => x.Age, f => f.Random.Int(18, 50))
   .RuleFor(x => x.Name, f => f.Person.FirstName)
   .CustomInstantiator(f =>
       new User(f.Person.Email)
       {
           Name = ...,
           Age = ...,
       });

Consider refactoring and using a non-typed Faker:

void Main()
{        
   User GenerateUser()
   {
      var f = new Faker();
      var user = new User(f.Person.Email)
               {
                  Name = f.Person.FirstName,
                  Age = f.Random.Int(18,50)
               };
              
      return user;
   }
             
   GenerateUser().Dump();
}

public class User
{
   public User(string email) => Email = email;
   
   public string Name { get; init; }
   public int Age { get; init; }
   public string Email { get; init; }
}

As you've indicated, Faker<T> has some limits before object design complexity gets in our way; and in such cases, we should consider a non-typed Faker as a suitable and reasonable alternative.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants