我们发现自己在许多测试用例中编写重复的夹具/模拟设置 - 就像这种情况:
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var encodingMock = fixture.Freeze<Mock<IEncodingWrapper>>();
var httpClientMock = fixture.Freeze<Mock<IHttpWebClientWrapper>>();
var httpResponseMock = fixture.Freeze<Mock<IHttpWebResponseWrapper>>();
var httpHeaderMock = fixture.Freeze<Mock<IHttpHeaderCollectionWrapper>>();
var etag = fixture.CreateAnonymous<string>();
byte[] data = fixture.CreateAnonymous<byte[]>();
Stream stream = new MemoryStream(data);
encodingMock.Setup(m => m.GetBytes(It.IsAny<string>())).Returns(data);
httpHeaderMock.SetupGet(m => m[It.IsAny<string>()]).Returns(etag).Verifiable();
httpClientMock.Setup(m => m.GetResponse()).Returns(httpResponseMock.Object);
httpResponseMock.Setup(m => m.StatusCode).Returns(HttpStatusCode.OK);
httpResponseMock.SetupGet(m => m.Headers).Returns(httpHeaderMock.Object);
httpResponseMock.Setup(m => m.GetResponseStream()).Returns(stream);
Run Code Online (Sandbox Code Playgroud)
根据测试应该是自包含的并且从头到尾可读的想法,我们不使用神奇的Setup/Teardown方法.
我们可以以任何方式(AutoFixture自定义,辅助方法)减少这些测试的"笨拙的工作"吗?
让我们考虑同一个非常简单的实体的两个版本(一个带有只读属性):
public class Client
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
与
public class Client
{
public Client(Guid id, string name)
{
this.Id = id;
this.Name = name;
}
public Guid Id { get; }
public string Name { get; }
}
Run Code Online (Sandbox Code Playgroud)
当我尝试使用Autofixture时,它们将正常工作,并且可以同时使用它们。当我尝试使用预先定义参数之一时,问题就开始了。with()
方法:
var obj = this.fixture.Build<Client>().With(c => c.Name, "TEST").Build();
Run Code Online (Sandbox Code Playgroud)
这将引发错误
System.ArgumentException:属性“名称”是只读的。
但是似乎Autofixture知道如何使用构造函数!而且看来实际Build<>()
方法不是创建对象的实例Create()
!如果构建仅准备具有设置属性的构建器,然后创建将实例化对象,则它将与只读属性一起正常工作。
那么为什么在这里使用这种(误导)策略呢?我在这里找到了一个答案,指出它是通过测试来放大反馈,但是我看不出使用的用处,FromFactory()
尤其是当参数列表很广时。在Build()
方法之间移动对象实例化Create()
会更直观吗?
我开始使用AutoFixture http://autofixture.codeplex.com/,因为我的单元测试因大量数据设置而膨胀.我花费更多时间来设置数据而不是编写单元测试.这是我的初始单元测试的样子的例子(例子来自DDD蓝皮书的货物申请样本)
[Test]
public void should_create_instance_with_correct_ctor_parameters()
{
var carrierMovements = new List<CarrierMovement>();
var deparureUnLocode1 = new UnLocode("AB44D");
var departureLocation1 = new Location(deparureUnLocode1, "HAMBOURG");
var arrivalUnLocode1 = new UnLocode("XX44D");
var arrivalLocation1 = new Location(arrivalUnLocode1, "TUNIS");
var departureDate1 = new DateTime(2010, 3, 15);
var arrivalDate1 = new DateTime(2010, 5, 12);
var carrierMovement1 = new CarrierMovement(departureLocation1, arrivalLocation1, departureDate1, arrivalDate1);
var deparureUnLocode2 = new UnLocode("CXRET");
var departureLocation2 = new Location(deparureUnLocode2, "GDANSK");
var arrivalUnLocode2 = new UnLocode("ZEZD4");
var arrivalLocation2 = new Location(arrivalUnLocode2, "LE HAVRE"); …
Run Code Online (Sandbox Code Playgroud) 此测试是否正确使用AutoFixture和Moq?它是尽可能简明扼要地写的吗?正如预期的那样,测试失败,并在写完正确的实现后通过.
[Fact]
public void CustomerPropertyIsCorrect()
{
var fixture = new AutoMoqFixture();
var expected = fixture.Create<CardHolderCustomer>();
var builderMock = fixture
.Freeze<Mock<ICustomerAdapter>>()
.Setup(x => x.BuildCustomer()).Returns(expected);
var sut = fixture.Create<CardHolderViewModel>();
var actual = sut.Customer;
Assert.Equal(expected, actual);
}
Run Code Online (Sandbox Code Playgroud) 我试图用Autofixture控制对象树的生成深度.在某些情况下,我只想生成根对象,在另一组情况下,我可能想要生成一个特定深度的树(2,3,比方说).
class Foo {
public string Name {get;set;}
public Bar Bar {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
}
class Bar {
public string Name {get;set;}
public string Description {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
public Xpto Xpto {get;set;}
}
class Xpto {
public string Description {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
}
Run Code Online (Sandbox Code Playgroud)
通过上面的例子,我希望(深度1)控制生成过程,以便只实例化Foo类,并且不填充该类的Bar属性或任何其他引用类型,或者(深度2)我想要Foo类实例化后,Bar属性使用新的Bar实例填充,但Xpto属性或该类上的任何其他引用类型未填充.
如果我没有在代码库中发现它,Autofixture是否有自定义或行为允许我们进行这种控制?
同样,我想要控制的不是递归,而是对象图的填充深度.
我正在使用 Autofixture 生成我的测试模型,但在一个实例中,它说我的模型有一个我找不到的循环引用。失败的模型是类RepresentationExpense
,我唯一能想到的是它与继承相关,因为该类Expense
没有相同的问题:
public class RepresentationExpense : Expense
{
public string GuestName { get; private set; }
public string GuestCompany { get; private set; }
public RepresentationExpense(ExpenseId id, ExpenseReportId reportId, EmployeeId employeeId, string description, Payment payment, DateTime created, PaymentMethod paymentMethod, ReceiptId receiptId, string guestName, string guestCompany, string comment = null)
: base(id, reportId, employeeId, description, payment, created, paymentMethod, CategoryType.Representation, receiptId, comment)
{
Verify.ArgumentNotNull(guestName, "guestName");
Verify.ArgumentNotNull(guestCompany, "guestCompany");
GuestName = guestName;
GuestCompany = guestCompany;
}
}
public class …
Run Code Online (Sandbox Code Playgroud) 我想创建foo的"很多"实例:
var fixture = new Fixture();
var expectedFoos = fixture.CreateMany<Foo>();
Run Code Online (Sandbox Code Playgroud)
问题是,Foo是一个实体框架实体,具有我不想创建的关系.如果我只需要一个实例,我可以这样做:
var fixture = new Fixture();
var expectedFoo = fixture.Build<Foo>()
.Without(foo => foo.Relation1);
.Without(foo => foo.Relation2);
Run Code Online (Sandbox Code Playgroud)
但是,如何轻松创建满足此条件的多个实例?我读过有关标本制造商的文章,但这看起来真的有些过分.
我正在寻找一些简单的东西(因为BuildMany
不存在而不能编译):
var fixture = new Fixture();
var expectedFoos = fixture.BuildMany<Foo>()
.Without(foo => foo.Relation1);
.Without(foo => foo.Relation2);
Run Code Online (Sandbox Code Playgroud) 我是否可以指示AutoFixture还填充私有属性,并使用特定属性(如Ninject.Inject
所有类)进行注释?该来源似乎只扫描公共财产:1.这个问题提供了一个特定MyClass
的私有设置器的解决方案,但不是私有财产或所有类:2.
我正在使用Moq来模拟服务,最后我想用这些模拟来填充属性.如果我将MyService依赖项公开为,则以下设置可以正常工作public
.
一些示例代码:
public class MyController {
[Inject]
private IMyService MyService { get; set; }
public void AMethodUsingMyService() {
MyService.DoSomething();
// ...
}
// ...
}
public class MyService : IMyService {
public void DoSomething()
{
// ...
}
// ...
}
public class MyControllerTest {
[Theory]
[AutoMoqData]
public void MyTest(MyController controller) {
controller.AMethodUsingMyService();
}
}
Run Code Online (Sandbox Code Playgroud) 生成的整数是IFixture.Create<int>()
唯一的吗?
维基说这些数字是随机的,但它也告诉我们这一点
第一个数字是在[1,255]范围内生成的,因为这是一组对所有数值数据类型有效的值..NET中最小的数字数据类型是System.Byte,它适合此范围.
当使用前255个整数时,随后从范围[256,32767]中选择数字,这对应于System.Int16可用的剩余正数.
GitHub上的两件相关事情:
https://github.com/AutoFixture/AutoFixture/issues/2
https://github.com/AutoFixture/AutoFixture/pull/7
那些单元测试怎么样?
https://github.com/AutoFixture/AutoFixture/blob/master/Src/AutoFixtureUnitTest/GeneratorTest.cs#L33
[Theory, ClassData(typeof(CountTestCases))]
public void StronglyTypedEnumerationYieldsUniqueValues(int count)
{
// Fixture setup
var sut = new Generator<T>(new Fixture());
// Exercise system
var actual = sut.Take(count);
// Verify outcome
Assert.Equal(count, actual.Distinct().Count());
// Teardown
}
Run Code Online (Sandbox Code Playgroud)
https://github.com/AutoFixture/AutoFixture/blob/master/Src/AutoFixtureUnitTest/GeneratorTest.cs#L57
[Theory, ClassData(typeof(CountTestCases))]
public void WeaklyTypedEnumerationYieldsUniqueValues(int count)
{
// Fixture setup
IEnumerable sut = new Generator<T>(new Fixture());
// Exercise system
var actual = sut.OfType<T>().Take(count);
// Verify outcome
Assert.Equal(count, actual.Distinct().Count());
// Teardown
}
Run Code Online (Sandbox Code Playgroud)
我还没有找到一个声明,说明生成的数字是唯一的,只有那些可能暗示它的信息,但我可能错了.
我正面临一个有趣的问题.我发现AutoDataAttribute可用于最小化测试的"排列"部分(通过ctor传递的依赖关系).真棒!
例:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(new Fixture().Customize(new AutoMoqCustomization()))
{ }
}
[Theory, AutoMoqData]
public void Process_ValidContext_CallsK2Workflows(
[Frozen]Mock<IK2Datasource> k2,
[Frozen]Mock<IAppConfiguration> config,
PrBatchApproveBroker sut)
{
(...)
}
Run Code Online (Sandbox Code Playgroud)
现在我想使用这个伟大的功能,并将自己的数据注入到这个理论中:
[Theory, AutoMoqData, MemberData("Data")]
public void ExtractPayments_EmptyInvoiceNumber_IgnoresRecordsWithEmptyInvoiceNumber(
[Frozen]Mock<IExcelDatasource> xls,
SunSystemExcelDatasource sut,
List<Row> rows,
int expectedCount)
{
(...)
}
Run Code Online (Sandbox Code Playgroud)
问题:AutoData属性将为我生成随机数据.我发现的唯一方法是摆脱AutoData属性并使用MemberData.如果我这样做,我需要自己处理对象实例化:))...
有没有办法同时传递我的类和一些"硬编码"数据?
谢谢你,Seb