【问题标题】:AutoFixture create property with internal setterAutoFixture 使用内部设置器创建属性
【发布时间】:2013-10-13 12:45:49
【问题描述】:

是否有某种方法可以让 AutoFixture 使用内部设置器创建属性?

我查看了 AutoFixture 源代码,发现在 AutoPropertiesCommand 中,GetProperties 方法检查属性是否具有 GetSetMethod() != null。 使用内部设置器,这将返回 null,除非您将 ignorePublic 参数设置为 true。

当然,最简单的方法是将 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);
    }
}

【问题讨论】:

  • 为什么不将 setter 公开是正确的解决方案?

标签: c# .net unit-testing autofixture


【解决方案1】:

理想情况下,测试不应该与类的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 的 support for Data Theories 让您的测试更加简洁:

[Theory, AutoData]
public void Test(Dummy dummy, string name)
{
    ((IModifiableDummy)dummy).SetName(name);
    // ...
}

如果您希望将Name 属性设置为已知 值,同时仍保持Dummy 对象的其余部分匿名,您还可以在同一数据中设置combine the two理论:

[Theory, InlineAutoData("SomeName")]
public void Test(string name, Dummy dummy)
{
    ((IModifiableDummy)dummy).SetName(name);
    // ...
}

【讨论】:

  • 感谢您的回答。我通过添加一个自定义的 ISpecimenBuilder 解决了这个问题,它创建了一个 Dummy 实例,然后遍历所有属性并为每个属性创建一个固定装置。这与 AutoPropertiesCommand 所做的几乎相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多