AutoFixture如何创建具有内部setter的属性

6
有没有一种方法可以让 AutoFixture 创建带有内部 setter 的属性?
我查看了 AutoFixture 源代码,并发现在 AutoPropertiesCommand 中,GetProperties 方法检查属性是否具有 GetSetMethod() != null。对于具有内部 setter 的属性,除非将 ignorePublic 参数设置为 true,否则此方法会返回 null。
当然,最简单的方法是使 setter 公开,但在我所在的项目中,这不是正确的解决方案。
下面是一个示例项目的简化代码。
public class Dummy
{
    public int Id { get; set; }
    public string Name { get; internal set; }
}

public class TestClass
{
    [Fact]
    public void Test()
    {
        var dummy = new Fixture().Create<Dummy>();
        Assert.NotNull(dummy.Name);
    }
}

2
为什么将setter公开不是正确的解决方案? - Mark Seemann
1个回答

5
理想情况下,测试不应该与类的internal成员交互,因为它们明确地被排除在其公共API之外。相反,这些成员将通过由公共API启动的代码路径间接测试。
然而,在您特定的情况下,如果这样做不可行,一个可能的解决方法是在测试中从内部属性中显式地分配一个值。
您可以通过以下两种方式之一来实现:
1.使用InternalsVisibleTo特性将程序集中的所有内部成员暴露给测试项目。
2.通过在特定接口中表示类的可修改状态并显式实现该接口。
在您的示例中,选项1将是:
// [assembly:InternalsVisibleTo("Tests")]
// is applied to the assembly that contains the 'Dummy' type

[Fact]
public void Test()
{
    var fixture = new Fixture();
    var dummy = fixture.Create<Dummy>();
    dummy.Name = fixture.Create<string>();
    // ...
}

相反,选项2可能是这样的:

public class Dummy : IModifiableDummy
{
    public string Name { get; private set; }

    public void IModifiableDummy.SetName(string value)
    {
        this.Name = value;
    }
}

[Fact]
public void Test()
{
    var fixture = new Fixture();
    var dummy = fixture.Create<Dummy>();
    ((IModifiableDummy)dummy).SetName(fixture.Create<string>());
    // ...
}

选项1实现起来相当快,但会导致打开程序集中的所有内部成员,这可能不是您想要的。
另一方面,选项2允许您控制应该公开哪些对象状态的可修改部分,同时仍将其与对象自身的公共API分离。
顺便提一下,由于您正在使用xUnit,您可以利用AutoFixture对数据理论的支持使测试更加简洁:(链接)
[Theory, AutoData]
public void Test(Dummy dummy, string name)
{
    ((IModifiableDummy)dummy).SetName(name);
    // ...
}

如果您希望在保持其余部分匿名的同时将Dummy对象的Name属性设置为已知值,则还可以在同一数据理论中将两者结合起来:
[Theory, InlineAutoData("SomeName")]
public void Test(string name, Dummy dummy)
{
    ((IModifiableDummy)dummy).SetName(name);
    // ...
}

谢谢你的回答。我通过添加自定义ISpecimenBuilder解决了这个问题,它创建了一个Dummy实例,然后循环遍历所有属性并为每个属性创建一个fixture。这与AutoPropertiesCommand所做的几乎相同。 - Marco van Kimmenade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接