我有很多使用MOQ的经验,而我最近偶然发现了AutoFixture.这些框架之间有什么区别?
在这种情况下,我正在为特定类型创建一个AutoFixture样本构建器System.Data.DataSet.构建器将返回a FakeDataSet,这是DataSet为测试而定制的.
以下不起作用,dataSet总是返回null,即使DataSet正在请求a(我可以通过钻取request属性来判断).
public class DataSetBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var dataSet = request as DataSet;
if (dataSet == null)
{
return new NoSpecimen(request);
}
return new FakeDataSet();
}
}
Run Code Online (Sandbox Code Playgroud)
这种变化确实有效,但似乎过于复杂.感觉有更好的方法来完成同样的事情,我只是缺少一些东西.
public class DataSetBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var seededRequest = request as SeededRequest;
if (seededRequest == null)
{
return new NoSpecimen(request);
}
var requestType = seededRequest.Request …Run Code Online (Sandbox Code Playgroud) 假设我有这个具体的类:
public partial class User
{
public int ID { get; set; }
public string Email { get; set; }
public string FullName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我想创建一个具有有效电子邮件地址的匿名实例,并且fullname字段不超过20个字符.我可以做这个:
var fixture = new Fixture();
var anonUser = fixture.Build<User>()
.With(x => x.Email, string.Format("{0}@fobar.com", fixture.Create<string>()))
.With(x => x.FullName, fixture.Create<string>()Substring(0,20))
.Create();
Run Code Online (Sandbox Code Playgroud)
有没有办法可以在一个地方定义它,所以AF知道我可以通过使用以下方式获得我的自定义anon类:
var newAnon = fixture.Build<User>();
Run Code Online (Sandbox Code Playgroud) 我有一个深度嵌套的对象模型,其中一些类可能看起来像这样:
class TBase { ... }
class TDerived : TBase { ... }
class Container
{
ICollection<TBase> instances;
...
}
class TopLevel
{
Container container1;
Container container2;
...
}
Run Code Online (Sandbox Code Playgroud)
我想将我的顶级对象创建为测试夹具,但我希望所有TBase实例(例如instances上面的集合中)都是实例TDerived而不是TBase.
我以为我可以简单地使用以下内容来做到这一点:
var fixture = new Fixture();
fixture.Customize<TBase>(c => c.Create<TDerived>());
var model = this.fixture.Create<TopLevel>();
Run Code Online (Sandbox Code Playgroud)
...但是这不起作用,因为lambda表达式Customize是错误的.我猜测有一种方法可以做到这一点,但AutoFixture似乎缺乏文档,而不是开发人员博客上的意识流.
谁能指出我正确的方向?
我需要创建一个具有两个具有相同名称的属性的类型,并且只在返回类型上有所不同.通过反射动态发射这种类型是完全可以接受的.
像这样的东西:
public TypeA Prop { get; }
public TypeB Prop { get; }
Run Code Online (Sandbox Code Playgroud)
我知道我不能从C#或VB.NET或许多其他.NET语言中使用这个属性.
为了防止答案向我解释为什么我不想这样做,让我解释一下为什么我需要它:我需要它来重现一个bug.
更具体地说,我在AutoFixture中有一个错误,其中一个类型的Moq会导致它在某些情况下抛出异常.问题是Moq发出的类型包含两个名为'Mock'的属性,它们仅在返回类型上有所不同.
我想在单元测试中重现这个场景,但是我不想仅仅因为这个原因而依赖于Moq,所以我想在测试套件内部重现这种行为.
我们发现自己在许多测试用例中编写重复的夹具/模拟设置 - 就像这种情况:
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自定义,辅助方法)减少这些测试的"笨拙的工作"吗?
现在找到解决方案的简短内容:
AutoFixture返回冻结模拟就好了; 我的sut也是由AutoFixture生成的,只有一个公共属性,其本地默认值对于测试非常重要,并且AutoFixture设置为新值.除了Mark的答案之外,还有很多值得学习的东西.
原始问题:
我昨天开始尝试使用AutoFixture进行我的xUnit.net测试,这些测试中包含了Moq.我希望更换一些Moq的东西或者让它更容易阅读,我特别感兴趣的是在SUT工厂容量中使用AutoFixture.
我使用Mark Seemann的一些关于AutoMocking的博客文章,并尝试从那里开始工作,但我没有走得太远.
这是我的测试看起来没有AutoFixture:
[Fact]
public void GetXml_ReturnsCorrectXElement()
{
// Arrange
string xmlString = @"
<mappings>
<mapping source='gcnm_loan_amount_min' target='gcnm_loan_amount_min_usd' />
<mapping source='gcnm_loan_amount_max' target='gcnm_loan_amount_max_usd' />
</mappings>";
string settingKey = "gcCreditApplicationUsdFieldMappings";
Mock<ISettings> settingsMock = new Mock<ISettings>();
settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);
ISettings settings = settingsMock.Object;
ITracingService tracing = new Mock<ITracingService>().Object;
XElement expectedXml = XElement.Parse(xmlString);
IMappingXml sut = new SettingMappingXml(settings, tracing);
// Act
XElement actualXml = sut.GetXml();
// Assert
Assert.True(XNode.DeepEquals(expectedXml, actualXml));
}
Run Code Online (Sandbox Code Playgroud)
这里的故事很简单 - 确保使用正确的密钥(这是硬编码/属性注入)SettingMappingXml查询ISettings依赖关系并将结果返回为XElement.在 …
假设我们将枚举类型定义为:
enum Statuses
{
Completed,
Pending,
NotStarted,
Started
}
Run Code Online (Sandbox Code Playgroud)
我想让Autofixture为我创造一个价值,而不是像Pending.
所以(假设循环生成)我想获得:
已完成,未启动,已启动,已完成,未启动,...
让我们考虑同一个非常简单的实体的两个版本(一个带有只读属性):
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 ×10
c# ×8
moq ×3
unit-testing ×3
automocking ×2
.net ×1
int ×1
mocking ×1
nunit ×1
random ×1