Closed vlamai closed 3 years ago
Hi @vlamai,
Your Foo
class will work by default. No changes or modifications are required for public protected set
properties as shown below:
void Main()
{
var fakeThings = new Faker<Foo>()
.RuleFor(x => x.Id, f => f.Random.Guid() );
var thing = fakeThings.Generate();
thing.Dump();
}
public class Foo
{
public Guid Id { get; protected set; }
}
However, setting a private
field via reflection is slightly harder. You'll need to use a PrivateBinder
class to reflect over type T
and extract the "private fields" of T
into the default discovery dictionary as shown below:
void Main()
{
var fooFaker = new Faker<Foo>()
.RuleFor(x => x.Id, f => f.Random.Guid() );
var fakeFoos = fooFaker.Generate(10);
var privateBinder = new PrivateBinder();
var barFaker = new Faker<Bar>(binder: privateBinder)
.RuleFor("foos", _ => fakeFoos);
barFaker.Generate().Dump();
}
public class PrivateBinder : Bogus.Binder
{
public override Dictionary<string, MemberInfo> GetMembers(Type t)
{
var members = base.GetMembers(t);
var privateBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
var allPrivateMembers = t.GetMembers(privateBindingFlags)
.OfType<FieldInfo>()
.Where( fi => fi.IsPrivate )
.Where( fi => !fi.GetCustomAttributes(typeof(CompilerGeneratedAttribute)).Any() )
.ToArray();
foreach( var privateField in allPrivateMembers ){
members.TryAdd(privateField.Name, privateField);
}
return members;
}
}
public class Bar
{
private readonly ICollection<Foo> foos = new HashSet<Foo>();
public ICollection<Foo> SomeCollection => foos;
}
public class Foo
{
public Guid Id { get; protected set; }
}
And that should do it. Check Issue #213 for even more ideas. :sweat_smile:
Thanks, Brian
thanks for answer @bchavez but in my imagination i set rule as
.RuleFor(x => x.SomeCollection, fooList)
in Binder it transform it to someCollection just by lower case first letter and set it
if usging string
.RuleFor("foos", _ => fakeFoos);
any rename will break all test where it used
Sorry if I'm asking a stupid question.
Sample code that put values to private collection by name of public
public class PrivateBinder : Binder
{
public override Dictionary<string, MemberInfo> GetMembers(Type t)
{
var members = base.GetMembers(t);
var privateBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
var allPrivateMembers = t.GetMembers(privateBindingFlags)
.OfType<FieldInfo>()
.Where( fi => fi.IsPrivate )
.Where( fi => !fi.GetCustomAttributes(typeof(CompilerGeneratedAttribute)).Any() )
.ToArray();
foreach( var privateField in allPrivateMembers ){
members.Add(privateField.Name, privateField);
}
return members;
}
}
public class DataFaker<T> : Faker<T> where T : class
{
public DataFaker(IBinder backingFieldBinder): base(binder: backingFieldBinder)
{
}
public DataFaker()
{
}
public DataFaker<T> UsingPrivate()
{
return base.CustomInstantiator(f => Activator.CreateInstance(typeof(T), nonPublic: true) as T)
as DataFaker<T>;
}
public virtual Faker<T> RuleForPrivate<TProperty>(Expression<Func<T, TProperty>> property, TProperty value)
{
var propName = PropertyName.For(property);
propName = propName.First()
.ToString()
.ToLower() +
propName.Substring(1);
return AddRule(propName, (f, t) => value);
}
}
var data =new DataFaker<Bar>(backingFieldBinder).UsingPrivate().RuleForPrivate(x => x.SomeCollection, value)
Version Information
What locale are you using with Bogus?
en
What's the problem?
Don't know how to set value for private collection
What possible solutions have you considered?
I try use reflection to set value for this field.
Do you have sample code to show what you're trying to do?
First i try to customize Facker
Sample classes
Sample test
Try to use custom IBinder, but i'm don't understned how it works. someCollection if result dictionary
Test with custom IBinder