【问题标题】:How to create fixture for sealed class with no public constructor?如何为没有公共构造函数的密封类创建夹具?
【发布时间】:2021-07-20 01:01:27
【问题描述】:

我已经密封了一个没有构造函数的类,我使用 SDK 将其引用到我的项目中。我想为类创建夹具数据以编写测试,但 AutoFixture 给出的期望如下所示。

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

请找到我正在尝试为其创建夹具的以下代码示例。

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; }
}

我通过 SDK 引用上述密封类,并且我正在尝试创建夹具数据。我收到以下错误消息。

消息: AutoFixture.ObjectCreationExceptionWithPath : AutoFixture 无法从 *** 创建实例,很可能是因为它没有公共构造函数,是抽象或非公共类型。

任何通用的解决方案?

【问题讨论】:

  • 你有剩下的类定义吗?猜测一下,您可能需要使用反射来创建实例。

标签: unit-testing mocking moq xunit autofixture


【解决方案1】:

鉴于它是密封的并且报告它没有公共构造函数,您将不得不提供一种创建实例的方法,并且它可能是基于反射的解决方案。只读属性也增加了 AutoFixture 的复杂性。

在没有看到构造函数的情况下,或者只读属性是否有任何支持字段的情况下,我将通过以下工作示例进行一些假设。

创建可以创建TokenCacheItem 并设置其属性的东西:

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;
    }
}

创建一个样本构建器以将其很好地固定到 AutoFixture 中:

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();
    }
}

添加自定义并开始使用:

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

工作 LINQPad 示例:

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() { }
}

结果:

YMMV 取决于实际的 TokenCacheItem 实现,尽管它可能并不遥远。

【讨论】:

    猜你喜欢
    • 2012-02-27
    • 1970-01-01
    • 1970-01-01
    • 2015-09-26
    • 2012-12-20
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多