Search code examples
c#unit-testingbogus

Bogus RuleFor private field


I have class with private readonly field

        public class Bar
        {
            private readonly ICollection<Foo> somePrivateCollection = new HashSet<Foo>();
            public IEnumerable<Foo> SomePublicCollection => somePrivateCollection;

            public int z;
        }

And

        public class Foo
        {
            public int x;
        }

I can set this field by GetField => SetValue

        [Fact]
        public void someTest()
        {
            var fooObjects = new Faker<Foo>().Generate(1);
            var barObject = new Faker<Bar>().RuleFor(t => t.z, f => f.Random.Int())
                                            .Generate();

            barObject.GetType()
                     .GetField("somePrivateCollection", System.Reflection.BindingFlags.NonPublic |
                                                                        System.Reflection.BindingFlags.Instance)
                     .SetValue(barObject, fooObjects);
        }

But how can I set this field using same style

            var barObject = new Faker<Bar>().RuleFor(t => t.z, f => f.Random.Int())
                                            .RuleSetter("somePrivateCollection",fooObjects)
                                            .Generate();

Solution

  • if difference between public and private field just in 1 letter

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