Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vue.js/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 单元测试大型代码块(映射、翻译等)_C#_Unit Testing_Etl - Fatal编程技术网

C# 单元测试大型代码块(映射、翻译等)

C# 单元测试大型代码块(映射、翻译等),c#,unit-testing,etl,C#,Unit Testing,Etl,我们对大部分业务逻辑进行单元测试,但仍停留在如何最好地测试一些大型服务任务和导入/导出例程上。例如,考虑将工资数据从一个系统导出到第三方系统。要以公司所需的格式导出数据,我们需要点击约40个表,这就造成了创建测试数据和模拟依赖关系的噩梦 例如,考虑下面的(出口代码的3500行子集): 在这个特定的导出类中,我们只有一个公共方法-ExportPaychecks()。这真的是唯一的行动,有任何意义的人调用这个类。。。其他一切都是私有的(约80个私有函数)。我们可以将它们公开测试,但是我们需要模拟它们

我们对大部分业务逻辑进行单元测试,但仍停留在如何最好地测试一些大型服务任务和导入/导出例程上。例如,考虑将工资数据从一个系统导出到第三方系统。要以公司所需的格式导出数据,我们需要点击约40个表,这就造成了创建测试数据和模拟依赖关系的噩梦

例如,考虑下面的(出口代码的3500行子集):

在这个特定的导出类中,我们只有一个公共方法-ExportPaychecks()。这真的是唯一的行动,有任何意义的人调用这个类。。。其他一切都是私有的(约80个私有函数)。我们可以将它们公开测试,但是我们需要模拟它们来分别测试每一个(即,如果不模拟WriteHeaderRow函数,就无法在真空中测试ExportPaychecks。这也是一个巨大的痛苦

由于这是一个单一的导出,对于单个供应商来说,将逻辑移到域中是没有意义的。该逻辑在这个特定类之外没有域意义。作为一个测试,我们构建了单元测试,其代码覆盖率接近100%…但这需要大量输入存根/模拟对象的测试数据,加上超过7000 li由于存根/模拟我们的许多依赖项,导致了大量代码

作为HRIS软件的制造商,我们有数百种进出口产品。其他公司真的对这类产品进行单元测试吗?如果是的话,有什么捷径可以让它不那么痛苦吗?我有点想说“没有对导入/导出例程进行单元测试”,只是在以后实施集成测试

更新-谢谢所有的答案。我很想看一个例子,因为我还不知道如何将大型文件导出之类的内容转换为易于测试的代码块,而不将代码弄得一团糟。

你有没有研究过

从网站上引用:

Moq(发音为“Mock you”或 “Mock”)是唯一的模拟库 NET从零开始发展到 充分利用.NET 3.5(即。 Linq表达式树)和C#3.0 特征(即lambda表达式) 这使得它最具生产力, 类型安全和重构友好 模拟库可用


我与C#无关,但我有一些想法,你可以在这里尝试。如果你稍微拆分一下代码,你会注意到你所做的基本上是对序列执行的一系列操作

第一个获得当前日期的付款:

    var pays = _pays.GetPaysForCurrentDate();
第二个无条件地处理结果

    foreach (PayObject pay in pays)
    {
       WriteHeaderRow(pay);
    }
第三个执行条件处理:

    foreach (PayObject pay in pays)
    {
       if (pay.IsFirstCheck)
       {
          WriteDetailRowType1(pay);
       }
    }
现在,您可以使这些阶段更通用(对不起,伪代码,我不知道C):


如您所见,现在您有了一组未连接的阶段,这些阶段可以单独测试,然后以任意顺序连接在一起。这种连接或组合也可以单独测试。以此类推(即,您可以选择要测试的内容)

我维护了一些与您描述的类似的报告,但数量没有您描述的那么多,数据库表也更少。我使用了一种三重策略,可以很好地扩展,以便对您有用:

  • 在方法层面,我对任何我主观上认为“复杂”的东西进行单元测试。这包括100%的错误修复,以及任何让我感到紧张的东西

  • 在模块级,我对主要用例进行单元测试。正如您所遇到的,这是相当痛苦的,因为它确实需要模拟数据。我通过抽象数据库接口(即,我的报告模块中没有直接的SQL连接)来完成这一点。对于一些简单的测试,我手动键入测试数据,对于其他测试,我编写了一个数据库接口,用于记录和/或回放查询,以便我可以使用真实数据引导测试。换句话说,我在记录模式下运行一次,它不仅获取真实数据,而且还将快照保存在文件中;当我在回放模式下运行时,它引用这个文件,而不是真正的数据库表。(我确信有模拟框架可以做到这一点,但因为我的世界中的每个SQL交互都有签名
    存储过程调用->记录集
    ,所以我自己编写就很简单了。)

  • 我很幸运能够访问一个具有生产数据完整副本的登台环境,因此我可以对以前的软件版本执行完整回归的集成测试


  • 我认为Tomasz Zielinski有一个答案,但如果你说你有3500行程序代码,那么问题就更大了。 将它分割成更多的函数并不能帮助您测试它。但是,这是确定可以进一步提取到另一个类的职责的第一步(如果您对方法有很好的名称,这在某些情况下是显而易见的)

    我猜有了这样一个类,你就有了一个令人难以置信的依赖项列表来处理,只是为了能够将这个类实例化到一个测试中。。。 Michael Feathers的《使用遗留代码》一书很好地回答了这些问题。 能够很好地测试代码的第一个目标应该是识别类的角色,并将其划分为更小的类。当然,这很容易说,讽刺的是,没有测试来确保修改是有风险的

    你说这个类中只有一个公共方法。这应该可以简化重构,因为你不需要担心所有私有方法的用户。封装很好,但是如果你在这个类中有那么多私有的东西,那可能意味着它不属于这里,你应该从这个怪物中提取不同的类,你可以最终将能够测试。一块一块地,设计应该看起来更干净,你将能够测试更多的大部件
        foreach (PayObject pay in pays)
        {
           if (pay.IsFirstCheck)
           {
              WriteDetailRowType1(pay);
           }
        }
    
        var all_pays = _pays.GetAll();
    
        var pwcdate = filter_pays(all_pays, current_date()) // filter_pays could also be made more generic, able to filter any sequence
    
        var pwcdate_ann =  annotate_with_header_row(pwcdate);       
    
        var pwcdate_ann_fc =  filter_first_check_only(pwcdate_annotated);  
    
        var pwcdate_ann_fc_ann =  annotate_with_detail_row(pwcdate_ann_fc);   // this could be made more generic, able to annotate with arbitrary row passed as parameter
    
        (Etc.)
    
    RuleParser  
    public evaluate(string)  
    private brachingExpression  
    private causalExpression  
    private variableExpression  
    private valueExpression  
    private nextTerm()  
    private hasMoreTerms()   
    public addVariables()  
    
    public void ExportPaychecks(HeaderFormatter h, CheckRowFormatter f)
    {
       var pays = _pays.GetPaysForCurrentDate();
       foreach (PayObject pay in pays)
       {
          h.formatHeader(pay);
          f.WriteDetailRow(pay);
       }
    }