C# 替代自动夹具自定义设置
我在一个类的构造函数中注入了几个服务。我将Autofixture与xUnit.net和NSubstitute一起使用,并创建了一个属性来设置全局自定义C# 替代自动夹具自定义设置,c#,unit-testing,autofixture,nsubstitute,C#,Unit Testing,Autofixture,Nsubstitute,我在一个类的构造函数中注入了几个服务。我将Autofixture与xUnit.net和NSubstitute一起使用,并创建了一个属性来设置全局自定义 public class AutoDbDataAttribute : AutoDataAttribute { public AutoDbDataAttribute() : base(() => new Fixture().Customize(new AutoNSubstituteCustomization())) {
public class AutoDbDataAttribute : AutoDataAttribute
{
public AutoDbDataAttribute() : base(() => new Fixture().Customize(new AutoNSubstituteCustomization()))
{
}
public AutoDbDataAttribute(Type customizationType) : base(() =>
{
var customization = Activator.CreateInstance(customizationType) as ICustomization;
var fixture = new Fixture();
fixture.Customize(new AutoNSubstituteCustomization());
fixture.Customize(customization);
return fixture;
})
{
}
}
我还有一个定制类,它为同一类中的测试方法设置公共定制
public class RevenueProviderCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register<IRevenueContextService>(() =>
{
var contextService = Substitute.For<IRevenueContextService>();
contextService.GetContext().Returns(fixture.Create<RevenueContext>());
return contextService;
});
fixture.Register<ICompanyService>(() =>
{
var companyService = Substitute.For<ICompanyService>();
companyService.Get(Arg.Any<Guid>()).Returns(fixture.Create<Company>());
return companyService;
});
}
}
但这不起作用。来自RevenueProviderCustomization
的RevenueContext
仍在使用
有人知道我如何覆盖服务的返回吗?我不想在测试中一个接一个地设置夹具,所以我希望能够创建一个“常规设置”,并根据测试用例进行必要的修改
更新1
试着从马克那里得到答案,我把测试改为
[Theory]
[AutoDbData(typeof(RevenueProviderCustomization))]
public void ShouldReturnCompanyRevenue([Frozen]IRevenueContextService contextService, [Frozen]Company company, RevenueProvider sut, RevenueContext context)
{
context.DepartmentId = null;
contextService.GetContext().Returns(context);
sut.GetRevenue().Should().Be(company.Revenue);
}
问题是因为RevenueProvider构造函数中调用了RevenueContext。所以我对DepartmentId的修改发生在电话打完之后
public RevenueProvider(IRevenueContextService contextService, ICompanyService companyService)
{
_contextService = contextService;
_companyService = companyService;
_company = GetCompany();
}
public double GetRevenue()
{
if (_hasDepartmentContext)
return _company.Departments.Single(d => d.Id == _departmentId).Revenue;
else
return _company.Revenue;
}
private Company GetCompany()
{
RevenueContext context = _contextService.GetContext();
if (context.DepartmentId.HasValue)
{
_hasDepartmentContext = true;
_departmentId = context.DepartmentId.Value;
}
return _companyService.Get(context.CompanyId);
}
假设
RevenueProvider
基本上如下所示:
[Theory]
[AutoDbData(typeof(RevenueProviderCustomization))]
public void ShouldReturnCompanyRevenue(RevenueProvider sut, Company company, [Frozen]IRevenueContextService contextService)
{
var fixture = new Fixture();
RevenueContext context = fixture.Build<RevenueContext>().With(c => c.DepartmentId, null).Create();
contextService.GetContext().Returns(context);
sut.GetRevenue().Should().Be(company.Revenue);
}
public class RevenueProvider
{
private readonly ICompanyService companySvc;
public RevenueProvider(ICompanyService companySvc)
{
this.companySvc = companySvc;
}
public object GetRevenue()
{
var company = this.companySvc.Get(Guid.Empty);
return company.Revenue;
}
}
然后通过以下测试:
[Theory]
[AutoDbData(typeof(RevenueProviderCustomization))]
public void ShouldReturnCompanyRevenue(
[Frozen]ICompanyService companySvc,
RevenueProvider sut,
Company company)
{
companySvc.Get(Arg.Any<Guid>()).Returns(company);
var actual = sut.GetRevenue();
Assert.Equal(company.Revenue, actual);
}
[理论]
[自动数据库数据(类型(RevenueProviderCustomization))]
公共无效应返回公司公平(
[冻结]我公司服务公司VC,
收入提供者sut,
(公司)
{
companySvc.Get(Arg.Any()).Returns(company);
var实际值=sut.GetRevenue();
断言。相等(公司收入、实际);
}
这个场景正是[freezed]
属性设计用来处理的。AutoFixture定义的各种属性按参数的顺序应用。这是出于设计,因为它使您能够在冻结类型之前从参数列表中提取一些值
在OP中,
[freezed]
仅在sut
之后应用,这就是模拟配置不适用于sut的原因。什么是“它不工作”?如果您显示了RevenueProvider,这将很有帮助,这样我们就可以了解您要测试的内容。基本上,RevenueProvider应该返回公司或部门的收入。取决于IRevenueContextService返回的RevenueContext。如果RevenueContext具有部门ID,则应返回该部门的收入。如果RevenueContext部门ID为空,则应返回公司的收入。这就是为什么在某些情况下我需要修改模拟IRevenueContextService返回的RevenueContext对象。因为我需要DepartmentId为Null,不幸的是仍然不起作用,我将IRevenueContextService更改为sut之前,但返回的RevenueContext仍然包含DepartmentId,请参阅文章中的更新1。问题是因为对RevenueContext的调用是作为构造函数的一部分完成的。因此,对DepartmentId的修改是在之后进行的。@AdeliaBenalius我不确定您对此能做多少,但是@AdeliaBenalius或者更确切地说,我想不出一种方法来解决[AutoData]
的问题,但仍然在测试本身中启用事后配置。我认为你要么必须改变构造函数,使它不做急迫的工作,要么必须以更迫切的方式编写测试。是的,我想我必须这样做。谢谢你的帮助,马克!