带有 AutoNSubstituteCustomization 的 AutoFixture:在 ReadOnly IEnumerable<t> 属性 上设置对象计数
AutoFixture with AutoNSubstituteCustomization: Set object count on ReadOnly IEnumerable<t> property
我的测试要求我在主实体集合的 IEnumerable 属性 中有不同数量的对象。我一直在搜索有关此的文档,但找不到任何内容。这是我的意思的示例(请注意,基本实体是使用 AutoNSubstituteCustomization
)
创建的
IFixture fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
var t = fixture.CreateMany<ITransaction>(5)
var service1 = Substitute.For<ITransactionsSvc>();
service1.GetTransactions().ReturnsForAnyArgs(t);
var service2 = Substitute.For<IRequestsSvc>();
service2.GetRequest(default).ReturnsForAnyArgs(
new Result(){
TransactionId = t.First().Files.First().RequestId
}
);
ITransaction
看起来像
public interface ITransaction
{
long RequestId { get; }
IEnumerable<FileDef> Files { get; }
IEnumerable<Comment> Comments { get; }
}
我知道我可以设置 fixture.RepeatCount
来指定全局计数,但我怎样才能为文件和评论设置不同的值?
我已经尝试使用 t.With(x => x.Files, () => fixture.CreateMany<FileDef>(rnd.Next(1,5))
但它抛出说这是只读 属性.
我也尝试在 t.Files
属性 上使用 NSubstitute .Returns
但由于某种原因, RequestId
的类型在尝试时从 Int64 更改为 Task`1读取 service2 ReturnForAnyArgs
响应的值。
我知道我必须从真实案例中删除一些复杂的内容,这样才能保持简洁,所以我希望我没有删除太多内容并使其易于理解。如果您需要任何精确度,欢迎询问。
子问题: AutoFixture有完整的文档吗?在 AutoFixture website 上,我只能找到非常介绍性的文档。
您遇到的问题似乎与 AutoFixture 无关,而与 NSubstitute 有关。
由于 ITransaction
是一个接口,AutoFixture 会将创建和实例的任务委托给模拟库。在你的情况下是 NSubstitute。
由于您的接口只声明了 getter 而没有 setters,NSubstitute 将为您的接口生成一个动态代理,它也没有任何 public setters .这就是 AutoFixture 无法设置您的属性值的原因。
因此,如果您想继续使用模拟,则必须在界面中指定 public setter 或告诉 AutoFixture 如何使用 NSubstitute [=32] 设置值=].不幸的是,您只能通过为您的界面实现一个 ISpecimenBuilder
工厂然后使用反射来实现第二个选项。
我推荐的另一种方法是将您的界面设置中继到一个伪造的实现,您将手动创建该实现并将具有 public setters .然后你将指示 AutoFixture 将所有请求转发到你的假 class.
接口
[Fact]
public void MyTest()
{
var fixture = new Fixture();
fixture.Customize<FakeTransaction>(c => c
.With(x => x.Files, fixture.CreateMany<FileDef>(2).ToList())
.With(x => x.Comments, fixture.CreateMany<Comment>(5).ToList()));
fixture.Customizations.Add(new TypeRelay(typeof(ITransaction), typeof(FakeTransaction)));
ITransaction mock2 = fixture.Create<ITransaction>();
Assert.Equal(2, mock2.Files.Count());
Assert.Equal(5, mock2.Comments.Count());
}
public class FakeTransaction : ITransaction
{
public long RequestId { get; set; }
public IEnumerable<FileDef> Files { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
提示:为了不在任何地方重复继电器,您可以创建一个将继电器添加到夹具的定制,然后使用 CompositeCustomization
将其与您当前的 NSubstitute 定制相结合。阅读更多 here.
关于你的第二个问题。不幸的是,这是唯一的“官方”文档。目前正在努力发布下一个版本。
更多信息可以参考维护者blogs and this community documentation site. Also there is a cool Pluralsight course available here。
事实上,正如@AndreiIvascu 提到的那样,我发现的问题和最干净的解决方案都与 NSubstitute 相关联。由于 NSubstitute 正在创建实例,因此可以使用标准 NSubstitute 调用来配置这些实例。
解决方案就是像我提到的那样使用 Returns 和 ReturnsForAnyArgs 但是 重要的是不要直接在访问 RequestId
.
时的第二个替代定义
请注意现在 new Result()
定义之外的行 var requestId = t.First().Files.First().RequestId;
。
public void MyTest()
{
IFixture fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
var t = fixture.Create<ITransaction>();
t.Files.Returns(fixture.CreateMany<FileDef>(2).ToList());
t.Comments.Returns(fixture.CreateMany<Comment>(5).ToList());
var service1 = Substitute.For<ITransactionsSvc>();
service1.GetTransactions().ReturnsForAnyArgs(t);
var requestId = t.First().Files.First().RequestId;
var service2 = Substitute.For<IRequestsSvc>();
service2.GetRequest(default).ReturnsForAnyArgs(
new Result(){
TransactionId = requestId
}
);
}
我的测试要求我在主实体集合的 IEnumerable 属性 中有不同数量的对象。我一直在搜索有关此的文档,但找不到任何内容。这是我的意思的示例(请注意,基本实体是使用 AutoNSubstituteCustomization
)
IFixture fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
var t = fixture.CreateMany<ITransaction>(5)
var service1 = Substitute.For<ITransactionsSvc>();
service1.GetTransactions().ReturnsForAnyArgs(t);
var service2 = Substitute.For<IRequestsSvc>();
service2.GetRequest(default).ReturnsForAnyArgs(
new Result(){
TransactionId = t.First().Files.First().RequestId
}
);
ITransaction
看起来像
public interface ITransaction
{
long RequestId { get; }
IEnumerable<FileDef> Files { get; }
IEnumerable<Comment> Comments { get; }
}
我知道我可以设置 fixture.RepeatCount
来指定全局计数,但我怎样才能为文件和评论设置不同的值?
我已经尝试使用 t.With(x => x.Files, () => fixture.CreateMany<FileDef>(rnd.Next(1,5))
但它抛出说这是只读 属性.
我也尝试在 t.Files
属性 上使用 NSubstitute .Returns
但由于某种原因, RequestId
的类型在尝试时从 Int64 更改为 Task`1读取 service2 ReturnForAnyArgs
响应的值。
我知道我必须从真实案例中删除一些复杂的内容,这样才能保持简洁,所以我希望我没有删除太多内容并使其易于理解。如果您需要任何精确度,欢迎询问。
子问题: AutoFixture有完整的文档吗?在 AutoFixture website 上,我只能找到非常介绍性的文档。
您遇到的问题似乎与 AutoFixture 无关,而与 NSubstitute 有关。
由于 ITransaction
是一个接口,AutoFixture 会将创建和实例的任务委托给模拟库。在你的情况下是 NSubstitute。
由于您的接口只声明了 getter 而没有 setters,NSubstitute 将为您的接口生成一个动态代理,它也没有任何 public setters .这就是 AutoFixture 无法设置您的属性值的原因。
因此,如果您想继续使用模拟,则必须在界面中指定 public setter 或告诉 AutoFixture 如何使用 NSubstitute [=32] 设置值=].不幸的是,您只能通过为您的界面实现一个 ISpecimenBuilder
工厂然后使用反射来实现第二个选项。
我推荐的另一种方法是将您的界面设置中继到一个伪造的实现,您将手动创建该实现并将具有 public setters .然后你将指示 AutoFixture 将所有请求转发到你的假 class.
接口[Fact]
public void MyTest()
{
var fixture = new Fixture();
fixture.Customize<FakeTransaction>(c => c
.With(x => x.Files, fixture.CreateMany<FileDef>(2).ToList())
.With(x => x.Comments, fixture.CreateMany<Comment>(5).ToList()));
fixture.Customizations.Add(new TypeRelay(typeof(ITransaction), typeof(FakeTransaction)));
ITransaction mock2 = fixture.Create<ITransaction>();
Assert.Equal(2, mock2.Files.Count());
Assert.Equal(5, mock2.Comments.Count());
}
public class FakeTransaction : ITransaction
{
public long RequestId { get; set; }
public IEnumerable<FileDef> Files { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
提示:为了不在任何地方重复继电器,您可以创建一个将继电器添加到夹具的定制,然后使用 CompositeCustomization
将其与您当前的 NSubstitute 定制相结合。阅读更多 here.
关于你的第二个问题。不幸的是,这是唯一的“官方”文档。目前正在努力发布下一个版本。
更多信息可以参考维护者blogs and this community documentation site. Also there is a cool Pluralsight course available here。
事实上,正如@AndreiIvascu 提到的那样,我发现的问题和最干净的解决方案都与 NSubstitute 相关联。由于 NSubstitute 正在创建实例,因此可以使用标准 NSubstitute 调用来配置这些实例。
解决方案就是像我提到的那样使用 Returns 和 ReturnsForAnyArgs 但是 重要的是不要直接在访问 RequestId
.
请注意现在 new Result()
定义之外的行 var requestId = t.First().Files.First().RequestId;
。
public void MyTest()
{
IFixture fixture = new Fixture().Customize(new AutoNSubstituteCustomization() { ConfigureMembers = true });
var t = fixture.Create<ITransaction>();
t.Files.Returns(fixture.CreateMany<FileDef>(2).ToList());
t.Comments.Returns(fixture.CreateMany<Comment>(5).ToList());
var service1 = Substitute.For<ITransactionsSvc>();
service1.GetTransactions().ReturnsForAnyArgs(t);
var requestId = t.First().Files.First().RequestId;
var service2 = Substitute.For<IRequestsSvc>();
service2.GetRequest(default).ReturnsForAnyArgs(
new Result(){
TransactionId = requestId
}
);
}