Search code examples
unit-testingmockingmoqxunitautofixture

How to create fixture for sealed class with no public constructor?


I have sealed a class with no constructor which is I am referred using SDK to my project. I want to create fixture data for the class to write test but AutoFixture is giving an expectation like below.

AutoFixture was unable to create an instance from SealedTypeclass, most likely because it has no public constructor, is an abstract or non-public type.

Please find the below code example for which I am trying to create a fixture.

public sealed class TokenCacheItem
{
    public string Authority { get; }
    public string ClientId { get; }
    public DateTimeOffset ExpiresOn { get; }
    public string FamilyName { get; }
    public string GivenName { get; }
    public string IdentityProvider { get; }
    public string TenantId { get; }
}

With the above-sealed class i am referring through SDK and I am trying to create fixture data. I am getting the below error message.

Message: AutoFixture.ObjectCreationExceptionWithPath : AutoFixture was unable to create an instance from ***, most likely because it has no public constructor, is an abstract or non-public type.

Any generic solution?


Solution

  • Given that it's sealed and reporting that it doesn't have a public constructor, you're going to have to provide a way to create an instance and it's probably going to be a reflection based solution. The read only properties add another complexity for AutoFixture as well.

    Without seeing the constructor, or whether there are any backing fields for the read only properties I'm going to make some assumptions with the following working example.

    Create something that can create a TokenCacheItem and set its properties:

    public class MutableTokenCacheItem
    {
        private TokenCacheItem _tokenCacheItem;
    
        public string Authority { get => _tokenCacheItem.Authority; set => SetPropertyValue(x => x.Authority, value); }
        public string ClientId { get => _tokenCacheItem.ClientId; set => SetPropertyValue(x => x.ClientId, value); }
        public DateTimeOffset ExpiresOn { get => _tokenCacheItem.ExpiresOn; set => SetPropertyValue(x => x.ExpiresOn, value); }
        public string FamilyName { get => _tokenCacheItem.FamilyName; set => SetPropertyValue(x => x.FamilyName, value); }
        public string GivenName { get => _tokenCacheItem.GivenName; set => SetPropertyValue(x => x.GivenName, value); }
        public string IdentityProvider { get => _tokenCacheItem.IdentityProvider; set => SetPropertyValue(x => x.IdentityProvider, value); }
        public string TenantId { get => _tokenCacheItem.TenantId; set => SetPropertyValue(x => x.TenantId, value); }
    
        public MutableTokenCacheItem()
        {
            var ctor = typeof(TokenCacheItem).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Single();
            _tokenCacheItem = (TokenCacheItem)ctor.Invoke(null);
        }
    
        private void SetPropertyValue<P>(Expression<Func<TokenCacheItem, P>> expression, object value)
        {
            var body = expression.Body as MemberExpression;
            var backingField = typeof(TokenCacheItem).GetRuntimeFields().Where(a => Regex.IsMatch(a.Name, $"\\A<{body.Member.Name}>k__BackingField\\Z")).Single();
            backingField.SetValue(_tokenCacheItem, value);
        }
    
        public TokenCacheItem AsImmutableTokenCacheItem()
        {
            return _tokenCacheItem;
        }
    }
    

    Create a specimen builder to bolt it into AutoFixture nicely:

    public class TokenCacheItemSpecimenBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var t = request as Type;
            if (typeof(TokenCacheItem).Equals(t))
            {
                var mutableTokenCacheItem = context.Create<MutableTokenCacheItem>();
                return mutableTokenCacheItem.AsImmutableTokenCacheItem();
            }
    
            return new NoSpecimen();
        }
    }
    

    Add the customization and off you go:

    var fixture = new Fixture();
    fixture.Customizations.Add(new TokenCacheItemSpecimenBuilder());
    fixture.Create<TokenCacheItem>();
    

    Working LINQPad example:

    void Main()
    {
        var fixture = new Fixture();
        fixture.Customizations.Add(new TokenCacheItemSpecimenBuilder());
        Console.Write(fixture.Create<TokenCacheItem>());
    }
    
    public class TokenCacheItemSpecimenBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var t = request as Type;
            if (typeof(TokenCacheItem).Equals(t))
            {
                var mutableTokenCacheItem = context.Create<MutableTokenCacheItem>();
                return mutableTokenCacheItem.AsImmutableTokenCacheItem();
            }
    
            return new NoSpecimen();
        }
    }
    
    public class MutableTokenCacheItem
    {
        private TokenCacheItem _tokenCacheItem;
    
        public string Authority { get => _tokenCacheItem.Authority; set => SetPropertyValue(x => x.Authority, value); }
        public string ClientId { get => _tokenCacheItem.ClientId; set => SetPropertyValue(x => x.ClientId, value); }
        public DateTimeOffset ExpiresOn { get => _tokenCacheItem.ExpiresOn; set => SetPropertyValue(x => x.ExpiresOn, value); }
        public string FamilyName { get => _tokenCacheItem.FamilyName; set => SetPropertyValue(x => x.FamilyName, value); }
        public string GivenName { get => _tokenCacheItem.GivenName; set => SetPropertyValue(x => x.GivenName, value); }
        public string IdentityProvider { get => _tokenCacheItem.IdentityProvider; set => SetPropertyValue(x => x.IdentityProvider, value); }
        public string TenantId { get => _tokenCacheItem.TenantId; set => SetPropertyValue(x => x.TenantId, value); }
    
        public MutableTokenCacheItem()
        {
            var ctor = typeof(TokenCacheItem).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Single();
            _tokenCacheItem = (TokenCacheItem)ctor.Invoke(null);
        }
    
        private void SetPropertyValue<P>(Expression<Func<TokenCacheItem, P>> expression, object value)
        {
            var body = expression.Body as MemberExpression;
            var backingField = typeof(TokenCacheItem).GetRuntimeFields().Where(a => Regex.IsMatch(a.Name, $"\\A<{body.Member.Name}>k__BackingField\\Z")).Single();
            backingField.SetValue(_tokenCacheItem, value);
        }
    
        public TokenCacheItem AsImmutableTokenCacheItem()
        {
            return _tokenCacheItem;
        }
    }
    
    public sealed class TokenCacheItem
    {
        public string Authority { get; }
        public string ClientId { get; }
        public DateTimeOffset ExpiresOn { get; }
        public string FamilyName { get; }
        public string GivenName { get; }
        public string IdentityProvider { get; }
        public string TenantId { get; }
    
        private TokenCacheItem() { }
    }
    

    Result:

    enter image description here

    YMMV depending on the actual TokenCacheItem implementation though it's probably not far off.