我们对大部分业务逻辑进行了单元测试,但仍坚持如何最好地测试我们的一些大型服务任务和导入/导出例程。例如,考虑将工资单数据从一个系统导出到第 3 方系统。要以公司需要的格式导出数据,我们需要打 ~40 个表,这为创建测试数据和模拟依赖项造成了噩梦。
例如,考虑以下内容(约 3500 行导出代码的子集):
public void ExportPaychecks()
{
var pays = _pays.GetPaysForCurrentDate();
foreach (PayObject pay in pays)
{
WriteHeaderRow(pay);
if (pay.IsFirstCheck)
{
WriteDetailRowType1(pay);
}
}
}
private void WriteHeaderRow(PayObject pay)
{
//do lots more stuff
}
private void WriteDetailRowType1(PayObject pay)
{
//do lots more stuff
}
我们在这个特定的导出类中只有一个公共(public)方法 - ExportPaychecks()。对于调用此类的人来说,这确实是唯一有意义的操作……其他所有内容都是私有(private)的(约 80 个私有(private)函数)。我们可以将它们公开进行测试,但随后我们需要模拟它们以分别测试每个(即,如果不模拟 WriteHeaderRow 函数,您就无法在真空中测试 ExportPaychecks。这也是一个巨大的痛苦。
由于这是单个导出,对于单个供应商而言,将逻辑移至域中没有意义。该逻辑在这个特定类之外没有域意义。作为测试,我们构建了接近 100% 代码覆盖率的单元测试……但这需要将大量测试数据输入到 stub /模拟对象中,加上 7000 多行代码,因为 stub /模拟我们的许多依赖项.
作为 HRIS 软件的制造商,我们有数百种导出和进口产品。其他公司真的对这类事情进行单元测试吗?如果是这样,是否有任何捷径可以减轻痛苦?我有点想说“没有对导入/导出例程进行单元测试”,只是稍后实现集成测试。
更新 - 感谢大家的回答。我很想看到一个例子,因为我仍然没有看到有人可以将大文件导出之类的东西变成易于测试的代码块,而不会将代码弄得一团糟。
最佳答案
这种(尝试的)单元测试风格,您试图通过一个公共(public)方法覆盖整个庞大的代码库,这总是让我想起外科医生、牙医或妇科医生,他们通过小开口执行复杂的操作。可能,但并不容易。
封装 是面向对象设计中的一个古老概念,但有些人将其极端化,导致可测试性受到影响。还有另一个 OO 原则称为 Open/Closed Principle这更适合可测试性。封装仍然很有值(value),但不会以可扩展性为代价 - 事实上,testability is really just another word for the Open/Closed Principle .
我并不是说您应该公开您的私有(private)方法,但我的意思是您应该考虑将您的应用程序重构为可组合的部分 - 许多协作的小类而不是一个大类 Transaction Script .您可能认为针对单个供应商的解决方案这样做没有多大意义,但现在您正在受苦,这是一种出路。
当您在复杂的 API 中拆分单个方法时,通常会发生的情况是您还会获得很多额外的灵 active 。最初的一次性项目可能会变成可重用的库。
以下是关于如何对手头的问题执行重构的一些想法:每个 ETL 应用程序都必须执行至少这三个步骤:
(因此,名称 ETL)。作为重构的开始,这给了我们至少三个具有不同职责的类:Extractor、Transformer 和 Loader。现在,您拥有三个具有更有针对性职责的类,而不是一个大类。没有什么乱七八糟的,而且已经更容易测试了。
现在放大这三个领域中的每一个领域,看看您可以在哪些方面进一步分担责任。
如果您有很多“行”的源数据和目标数据,您可以在映射器中为每个逻辑“行”等进一步拆分。
它永远不需要变得凌乱,而且额外的好处(除了自动化测试之外)是对象模型现在更加灵活。如果您需要编写涉及这两个方面之一的另一个 ETL 应用程序,您已经编写了至少三分之一的代码。
关于c# - 单元测试大块代码(映射、翻译等),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2076398/
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test
我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r