C# 结构化测试代码

C# 结构化测试代码,c#,unit-testing,xunit,nsubstitute,C#,Unit Testing,Xunit,Nsubstitute,目前,我们正在尝试对我们的服务实施一些单元测试。在下面的服务中,将创建订单,并对订单的创建进行审核注册。在编写这两个测试时(因为我们认为应该将测试分开,以获得具有1个责任的测试),这就是我开始的地方: public class TestPacklineOrderManagementService { [Fact] public void CreateNewProductWhenNoPacklineOrderIsAvailable() { IPackLi

目前,我们正在尝试对我们的服务实施一些单元测试。在下面的服务中,将创建订单,并对订单的创建进行审核注册。在编写这两个测试时(因为我们认为应该将测试分开,以获得具有1个责任的测试),这就是我开始的地方:

public class TestPacklineOrderManagementService
{

    [Fact]
    public void CreateNewProductWhenNoPacklineOrderIsAvailable()
    {

        IPackLineOrderRepository packLineOrderRepository = Substitute.For<IPackLineOrderRepository>();
        packLineOrderRepository.GetActive(Arg.Any<PackLine>()).Returns(x => null);
        var rawProductRepository = Substitute.For<IRawProductRepository>();
        rawProductRepository.Get(1).Returns(new RawProduct {Id = 1});
        var packlineRepository = Substitute.For<IPackLineRepository>();
        packlineRepository.Get(1).Returns(new PackLine {Id = 1});
        var auditRegistrationService = Substitute.For<IAuditRegistrationService>();
        var packlineOrderManagementService = new PacklineOrderManagementService(packLineOrderRepository, rawProductRepository, packlineRepository, auditRegistrationService);

        packlineOrderManagementService.SetProduct(1,1);

        packLineOrderRepository.Received()
            .Insert(Arg.Is<PackLineOrder>(x => x.PackLine.Id == 1 && x.Product.Id == 1));
    }

    [Fact]
    public void AuditCreateNewProductWhenNoPacklineOrderIsAvailable()
    {
        IPackLineOrderRepository packLineOrderRepository = Substitute.For<IPackLineOrderRepository>();
        packLineOrderRepository.GetActive(Arg.Any<PackLine>()).Returns(x=>null);
        var rawProductRepository = Substitute.For<IRawProductRepository>();
        rawProductRepository.Get(1).Returns(new RawProduct { Id = 1 });
        var packlineRepository = Substitute.For<IPackLineRepository>();
        packlineRepository.Get(1).Returns(new PackLine { Id = 1 });
        var auditRegistrationService = Substitute.For<IAuditRegistrationService>();
        var packlineOrderManagementService = new PacklineOrderManagementService(packLineOrderRepository, rawProductRepository, packlineRepository, auditRegistrationService);

        packlineOrderManagementService.SetProduct(1, 1);

        auditRegistrationService.Received()
            .Audit(Arg.Is<PackLineOrderAudit>(item => item.Action == PackLineOrderAction.CreatePacklineOrder));
    }
}
公共类TestPacklineOrderManagementService
{
[事实]
public void CreateNewProductWhenOpacklineOrderisAvailable()时创建新产品
{
IPackLineOrderRepository packLineOrderRepository=替换.For();
GetActive(Arg.Any())。返回(x=>null);
var rawProductRepository=Substitute.For();
获取(1).Returns(新的RawProduct{Id=1});
var packlineRepository=替换.For();
packlineRepository.Get(1).Returns(新的PackLine{Id=1});
var auditRegistrationService=Substitute.For();
var packlineOrderManagementService=新的packlineOrderManagementService(packlineorderepository、rawProductRepository、packlineRepository、auditRegistrationService);
packlineOrderManagementService.SetProduct(1,1);
packlineorderepository.Received()
.Insert(参数为(x=>x.PackLine.Id==1&&x.Product.Id==1));
}
[事实]
public void AuditCreateNewProductWhenOpacklineOrderIsAvailable()可用时
{
IPackLineOrderRepository packLineOrderRepository=替换.For();
GetActive(Arg.Any())。返回(x=>null);
var rawProductRepository=Substitute.For();
获取(1).Returns(新的RawProduct{Id=1});
var packlineRepository=替换.For();
packlineRepository.Get(1).Returns(新的PackLine{Id=1});
var auditRegistrationService=Substitute.For();
var packlineOrderManagementService=新的packlineOrderManagementService(packlineorderepository、rawProductRepository、packlineRepository、auditRegistrationService);
packlineOrderManagementService.SetProduct(1,1);
auditRegistrationService.Received()
.Audit(Arg.Is(item=>item.Action==PackLineOrderAction.CreatePacklineOrder));
}
}
正如您可以看到的,有很多重复的代码。为了防止出现这种情况,我尝试对其进行重构,结果产生了以下代码:

public class TestPacklineOrderManagementService2
{

    [Fact]
    public void CreateNewProductWhenNoPacklineOrderIsAvailable()
    {
        IPackLineOrderRepository packLineOrderRepository;
        IAuditRegistrationService auditRegistrationService;
        var packlineOrderManagementService = BuilderForCreateNewProductWhenNoPacklineOrderIsAvailable(out packLineOrderRepository, out auditRegistrationService);

        packlineOrderManagementService.SetProduct(1,1);

        packLineOrderRepository.Received().Insert(Arg.Any<PackLineOrder>());
    }

    [Fact]
    public void AuditCreateNewProductWhenNoPacklineOrderIsAvailable()
    {
        IPackLineOrderRepository packLineOrderRepository;
        IAuditRegistrationService auditRegistrationService;
        var packlineOrderManagementService = BuilderForCreateNewProductWhenNoPacklineOrderIsAvailable(out packLineOrderRepository, out auditRegistrationService);

        packlineOrderManagementService.SetProduct(1, 1);

        auditRegistrationService.Received()
            .Audit(Arg.Is<PackLineOrderAudit>(item => item.Action == PackLineOrderAction.CreatePacklineOrder));
    }

    private PacklineOrderManagementService BuilderForCreateNewProductWhenNoPacklineOrderIsAvailable(out IPackLineOrderRepository packLineOrderRepository,
       out IAuditRegistrationService auditRegistrationService)
    {
        packLineOrderRepository = CreatePackLineOrderRepository(x => null);
        auditRegistrationService = CreateAuditRegistrationService();

        var rawProductRepository = CreateRawProductRepository(x => new RawProduct { Id = 1 });
        var packlineRepository = CreatePacklineRepository(x => new PackLine { Id = 1 });
        var packlineOrderManagementService = new PacklineOrderManagementService(packLineOrderRepository,
            rawProductRepository, packlineRepository, auditRegistrationService);

        return packlineOrderManagementService;
    }

    private IPackLineOrderRepository CreatePackLineOrderRepository(Func<CallInfo, PackLineOrder> getActiveResult)
    {
        IPackLineOrderRepository packLineOrderRepository = Substitute.For<IPackLineOrderRepository>();
        packLineOrderRepository.GetActive(Arg.Any<PackLine>()).Returns(getActiveResult);
        return packLineOrderRepository;
    }

    private IRawProductRepository CreateRawProductRepository(Func<CallInfo, RawProduct> getResult)
    {
        IRawProductRepository rawProductRepository = Substitute.For<IRawProductRepository>();
        rawProductRepository.Get(1).Returns(getResult);
        return rawProductRepository;
    }

    private IPackLineRepository CreatePacklineRepository(Func<CallInfo, PackLine> getResult)
    {
        IPackLineRepository packLineRepository = Substitute.For<IPackLineRepository>();
        packLineRepository.Get(1).Returns(getResult);
        return packLineRepository;
    }

    private IAuditRegistrationService CreateAuditRegistrationService()
    {
        return Substitute.For<IAuditRegistrationService>();
    }
}
公共类TestPacklineOrderManagementService2
{
[事实]
public void CreateNewProductWhenOpacklineOrderisAvailable()时创建新产品
{
IPackLineOrderRepository packLineOrderRepository;
IAuditRegistrationService审核注册服务;
var packlineOrderManagementService=builderforcreatenewproductwhenopacklineorderisavailable(out packLineOrderRepository,out auditRegistrationService);
packlineOrderManagementService.SetProduct(1,1);
packlineorderepository.Received().Insert(Arg.Any());
}
[事实]
public void AuditCreateNewProductWhenOpacklineOrderIsAvailable()可用时
{
IPackLineOrderRepository packLineOrderRepository;
IAuditRegistrationService审核注册服务;
var packlineOrderManagementService=builderforcreatenewproductwhenopacklineorderisavailable(out packLineOrderRepository,out auditRegistrationService);
packlineOrderManagementService.SetProduct(1,1);
auditRegistrationService.Received()
.Audit(Arg.Is(item=>item.Action==PackLineOrderAction.CreatePacklineOrder));
}
当OpacklineOrderOrderRepository packLineOrderRepository可用时,private PacklineOrderManagementService Builder可用于创建新产品,
out IAUDITREGISTRATION服务审核注册服务)
{
packlineorderepository=createpacklineorderepository(x=>null);
auditRegistrationService=CreateAuditRegistrationService();
var rawProductRepository=CreateRawProductRepository(x=>newRawProduct{Id=1});
var packlineRepository=CreatePacklineRepository(x=>newpackline{Id=1});
var packlineOrderManagementService=新的packlineOrderManagementService(packlineorderepository,
rawProductRepository、packlineRepository、auditRegistrationService);
返回packlineOrderManagementService;
}
专用IPackLineOrderRepository CreatePackLineOrderRepository(Func getActiveResult)
{
IPackLineOrderRepository packLineOrderRepository=替换.For();
GetActive(Arg.Any())。返回(getActiveResult);
返回packlineordrepository;
}
专用IRawProductRepository CreateRawProductRepository(Func getResult)
{
IRawProductRepository rawProductRepository=Substitute.For();
rawProductRepository.Get(1).Returns(getResult);
返回资源库;
}
专用IPackLineRepository CreatePacklineRepository(Func getResult)
{
IPackLineRepository packLineRepository=Substitute.For();
packLineRepository.Get(1).返回(getResult);
退货包装;
}
私有IAuditRegistrationService CreateAuditRegistrationService()
{
返回replacement.For();
}
}
有没有办法为我们的单元测试提供更好的代码基础

更好是非常主观的,这在很大程度上取决于你如何定义它。有些人可能会认为第一个示例更好,因为所有的设置代码都在测试中。根据你上面的代码,我确实有一些反馈

在编写测试时,不要对测试中的系统(SUT)的两个参数使用相同的值,除非它们确实相同,否则会隐藏转置错误。所以,在测试中,你要设置一个替代品,如下所示:

rawProductRepository.Get(1).Returns(new RawProduct {Id = 1});
public class TestPacklineOrderManagementService
{
    public class TestSetProduct {
        IPackLineOrderRepository _packLineOrderRepository;
        IRawProductRepository _rawProductRepository;
        // etc
        public TestSetProduct() {
            _packLineOrderRepository = Substitute.For<IPackLineOrderRepository>();
            _packLineOrderRepository.GetActive(Arg.Any<PackLine>()).Returns(x => null);
            _rawProductRepository = Substitute.For<IRawProductRepository>();
            // etc
        }
        [Fact]
        public void CreateNewProductWhenNoPacklineOrderIsAvailable()
        {
            // Any test specific setup...               

            _packlineOrderManagementService.SetProduct(1,1);

            _packLineOrderRepository.Received()
                .Insert(Arg.Is<PackLineOrder>(x => x.PackLine.Id == 1 
                                                && x.Product.Id == 1));
        }       
        [Fact]
        public void AuditCreateNewProductWhenNoPacklineOrderIsAvailable()
        {
            _packlineOrderManagementService.SetProduct(1, 1);

            _auditRegistrationService.Received()
                .Audit(Arg.Is<PackLineOrderAudit>(item => 
                               item.Action == PackLineOrderAction.CreatePacklineOrder));
        }
    }
    public class TestSomeOtherScenario {
        // tests...
    }
}
然后给你的SUT打电话:

packlineOrderManagementService.SetProduct(1,1);
SUT调用中的1是否与存储库设置相关?一点也不清楚哪个1是哪个

这有点主观,但如果您的测试设置完全相同,您真的需要使用不同的断言复制测试吗?真的吗