使用AutoFixture 3.50和xUnit.NET,似乎 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<ListView android:id="@android:id/list" //Important this one.
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00FF00"
android:layout_weight="1"
android:drawSelectorOnTop="false"/>
</LinearLayout>
创建具体对象的方式与AutoData Theory测试创建具体对象的方式之间存在差异。
简单示例:
Fixture.Create()
使用public class Foo
{
private string prop;
public string Prop
{
get
{
if (prop == null) { prop = "Prop"; } // Breakpoint 'A'
return prop;
}
}
}
进行测试:
Fixture
使用[Fact]
public void FixtureTest()
{
var fixture = new Fixture();
var result = fixture.Create<Foo>(); // Breakpoint 'B1'
}
进行测试:
AutoDataAttribute
在前一个测试中,断点'B1'被击中,而断点'A'从未被击中。在后一个测试中,断点'A'在断点'B2'被击中之前被击中。当我的“懒惰”初始化属性与上面的属性不同时,这是有问题的 - 因为在运行测试之前初始化了属性的支持字段,我无法测试初始化逻辑。
有没有办法自定义[Theory, AutoData]
public void AutoDataTest(Foo sut)
{
var bar = 1; // Essential no-op, Breakpoint 'B2'
}
以便我可以绕过这种行为?或者可能,这可能是一个错误?
答案 0 :(得分:4)
事实上,这不是一个AutoFixture问题,而是一个xUnit.net问题,如果你愿意的话。您可以完全重现它,不需要像这样的AutoFixture:
[Theory, ClassData(typeof(FooTestCases))]
public void ClassDataTest(Foo sut)
{
var bar = 1; // Essential no-op, Breakpoint 'B3'
}
private class FooTestCases : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new Foo() };
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
如果你调试到这个ClassDataTest
,你也会在遇到断点'B3'之前点击断点'A'。
原因是xUnit.net测试运行器希望为每个参数化测试提供一个好的显示名称,因此它尝试创建一个可读的字符串表示形式,传递给{{1}中每个测试用例的所有参数}。
当数据对象未显式覆盖[Theory]
时,xUnit.net将回退到读取所有属性并从中构建显示字符串。这就是这里发生的事情。
我已经尝试搜索该行为的一些官方文档,但我能找到的最好的是this。正如那里建议的那样,你可以通过覆盖ToString
:
ToString
此更改会在使用public class Foo
{
private string prop;
public string Prop
{
get
{
if (prop == null) { prop = "Prop"; } // Breakpoint 'A'
return prop;
}
}
public override string ToString()
{
return "Foo";
}
}
和ClassData
时阻止断点'A'被击中。是否要覆盖AutoData
是另一个问题。
如果您不想覆盖ToString
上的ToString
,也许您可以通过测试覆盖Foo
的特定于测试的子类来解决此问题,如下所示:
ToString
这也可以阻止断点'A'被击中。
只要[Theory, AutoData]
public void AutoDataTestFoo(TestFoo sut)
{
var bar = 1; // Essential no-op, Breakpoint 'B4'
}
public class TestFoo : Foo
{
public override string ToString()
{
return "Foo";
}
}
不是Foo
,您就应该能够做到这一点。如果sealed
为Foo
,我无法想到任何其他解决方法,而不是以sealed
的方式编写测试。