理想情况下,测试不应该与internal
类的成员交互,因为它们被明确排除在其公共 API 之外。相反,这些成员将通过公共 API 启动的代码路径进行间接测试。
但是,如果这在您的特定情况下不可行,则可能的解决方法是从测试中显式地为内部属性分配一个值。
您可以通过以下两种方式之一做到这一点:
- 通过使用该属性将程序集中的所有内部成员公开给测试项目。
InternalsVisibleTo
- 通过在特定接口中表示类的可修改状态并显式实现它。
在您的示例中,选项 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);
// ...
}
如果您希望将Name
属性设置为已知值,同时仍然保持对象的其余部分Dummy
匿名,您还可以在同一个 Data Theory中将两者结合起来:
[Theory, InlineAutoData("SomeName")]
public void Test(string name, Dummy dummy)
{
((IModifiableDummy)dummy).SetName(name);
// ...
}