有点上下文:今天早些时候,我不得不更新我的另一个同事提供的一些SQL代码,由于它是一个很大的脚本,因此将其存储为单独的文件(然后在运行时读取并执行)。在执行此操作时,我不小心重新引入了几个月前的两个错误,即:

出于任何原因,ASCII文件都以UTF-16编码(同事通过电子邮件向我发送了该文件,这可能导致了该错误) )。
脚本缺少初始SET语句(由于生产中存在一些驱动程序的原因,而并非本地进行全新安装,因此该语句是必需的。)

调试了大约一个小时后(再次)我决定编写一些单元测试以确保再也不会发生这种情况(并在断言消息中包含一种快速修复它的方法,以为将来的开发人员提供简单的修复方法。)
但是当我将此代码推送给另一个同事时(谁也是我们的团队负责人)走到我面前,告诉我我不应该再做这些事情,因为:

“这些东西不属于单元测试”
“单元测试应该只用于检查代码流。“

我现在很矛盾,因为我仍然认为我在做的事没有错,因为此bug不会被reintr但是,这位同事是大四学生,最终决定了我们花些时间在哪里。我该怎么办?我这样做不对吗?它被认为是不好的做法吗?

评论

可能重复的项目是否应编写自动化测试以检查数据是否正确(配置文件,数据库等的内容)?

“单元测试仅应用于检查代码流”,这是胡说八道。传统上,它们应包括所有必要的测试,以确保单独考虑的“单元”是正确的。如果您只编写对“检查流程”有用的那些单元测试,那么无论如何,我希望您也有单独的扩展测试套件(由QA部门撰写?)。

无论如何,您同事的问题很可能就是您进行这些测试的地方。我将专注于此,不讨论教派/圣战。对于您添加到套件中的套件来说,这些测试可能太慢了,但是您的同事也完全有可能只盯着他关于单元测试的想法,而从一个不存在的问题中提出一个问题。因此最好先弄清楚真正的问题是什么。

顺便说一下,这些测试确实看起来像您每次修改该SQL文件时要运行的东西。这里的主要问题可能是测试工具,它可能不支持“仅在修改后才能运行”操作模式。它会引起实际的具体问题,因此值得手动添加“仅在经过修改的情况下”功能,而对于那些特定的测试则需要一些技巧。

为什么不测试文件是否具有正确的内容和编码,而不是测试文件是否有效?

#1 楼

您编写的测试很可能比单元测试更接近集成或回归测试。尽管这条线可能很模糊,有时会成为关于什么是单元测试或不是单元测试的法,但我还是回到您的同事那里,问您编写的测试应该在哪里,因为它们确实可以确保代码正确性。 br />
我不会过多地关注什么是单元测试或什么不是单元测试,并且意识到即使是集成测试,该测试中仍然会有价值。

评论


评论不作进一步讨论;此对话已移至聊天。

–托马斯·欧文斯♦
17年11月5日在17:20

#2 楼

从技术上讲,它不是单元测试,而是更多的验证步骤。正确的方法实际上取决于您的工作流程需要。您的团队负责人对单元测试的目的是正确的。我的感觉是,这是在仍需要完成的工作中使用错误工具的情况。所以从这个开始:


我要解决的问题是什么?


通过描述,您需要验证任何数据库脚本符合某些标准。


有哪些工具/过程可以用来解决问题?


源代码质量通常通过静态分析来检查。工具。如果没有静态分析工具来验证SQL,则可以创建一个快速而肮脏的工具,对传入的任何SQL文件进行检查。检查是否有静态分析工具可以处理您正在谈论的问题,这没有什么坏处。

如果您将构建基础架构的一部分纳入其中,例如将其合并到Jenkins或类似工具中,这样,它就可以应用于项目中的所有SQL文件。

单元测试只能解决当前文件的问题。


我该如何交流是否需要该工具?


与您的团队负责人交谈这很容易。他可以与产品负责人和您一起确定投资工具的风险/回报。如果这可能是一次性的问题,那么该工具可能会过大。如果解决最大问题的工具很容易,那么为进行健全性检查就值得。

您的团队负责人可能有一些您(或我)没有考虑过的想法,可以更正确地解决问题。

评论


在讨论成本与风险的关系时,请务必注意,由于重新引入了先前解决的错误,它已经造成了时间的浪费。仅此一项就可以使验证自动化。 +1

– jpmc26
17-10-31在0:59



@ jpmc26,我完全同意。讨论应从以下事实开始:您花了很多时间弄清楚问题出在哪里,而单元测试是您防止团队其他成员损失相同时间的第一个尝试。但是,通常与与负责管理和利益相关者的团队负责人合作很受赞赏。作为团队负责人,我希望能够捍卫我们管理的工具/实践/代码。如果我看到无法追溯到实际需求的单元测试,我也会很担心。

–贝琳·洛里奇(Berin Loritsch)
17-10-31在13:22

@BerinLoritsch从技术上讲,这不是单元测试,我真的很想知道您基于此断言使用的是什么“技术”。据我所知,对单元测试没有单一的权威性定义,每个人对“它们是什么”都有自己的看法。

– gbr
17-10-31在13:37



@gbr开发人员之间有一项非正式协议,即单元测试是独立于外部系统运行的测试。他们仅测试代码本身,而不测试与文件,数据库或其他外部服务的交互。 Wikipedia确实记录了这种理解:en.wikipedia.org/wiki/Unit_testing#External_work。

– jpmc26
17年10月31日在18:35



@BerinLoritsch还有可能我们都以不同的方式解释了这个问题,无论如何,它不是很详细,作者还没有再找任何人。无论如何,我对讨论这些测试的分类不是很感兴趣,重要的是它们是否应该存在(我很确定它们应该存在)以及运行它们的频率(在IDE中的每次更改,每次本地提交,每次推送到中央存储库,每次都这样...)。

– gbr
17年11月2日在17:34



#3 楼

将访问文件的测试称为“单元测试”是一种不好的做法。

他:“这些东西不属于单元测试”
您:“说得通,但我做不到找不到合适的地方。它们属于什么地方?”

不幸的是,存在哪些测试以及如何组织这些测试完全取决于公司。因此,您需要了解贵公司如何处理这些测试。
如果除单元测试之外,您还没有运行自动化测试的方法,那么务实的方法是标记实际上不是真正的单元测试。带有前缀的单元测试,直到您有足够的数量来开始弄清楚您实际/需要哪种测试。之后,您就可以开始整理了。

评论


将访问文件的测试称为“单元测试”是一种不好的做法。这里正在访问的文件是源文件。它将像访问任何源文件一样进行访问(以对其进行解析)。测试可能不会以与被检查的“单元”(SQL)相同的语言进行,这一事实使它不合常规,但不应影响其作为单元测试的分类。继续...

– gbr
17-10-31在14:08



...实际上,文件的正确编码是任何编译器每次读取源文件时都进行的“测试”。这里的问题是,作为一个在运行时解释的外部文件,“编译器测试”将不会自动运行,因此完全将它们显式添加是完全适当的,我认为可以将其视为该文件的“单元测试” SQL代码段。将它包含在每次修改文件时运行的(潜在)测试套件中似乎是合理的。

– gbr
17-10-31在14:08

顺便说一句,通常建议不要访问外部文件,只要外部文件可以用模拟或类似的东西代替。而且,按照大多数定义,单元测试可以访问外部文件或任何其他内容,强烈建议不要使用它,因为它会大大降低速度。商店可以自由地规定您不能将访问文件的测试添加到运行最频繁的测试套件中,但这并不能使此类测试不值得“单元测试”的称号,它们只是使它们“不”放在该项目最常运行的测试套件中”。

– gbr
17-10-31在14:08



@Warbo(通常)访问文件是一种不好的做法,(最重要的)原因是,如果文件涉及“通过不稳定的NFS链接读取的GB”,则它们的运行速度并不慢;例如,如果访问“引用迈克尔·费瑟斯(Michael Feathers)的话,它们需要1/10秒的时间。这是因为您希望尽可能频繁地运行测试,理想情况下是在IDE中进行的每一次更改,并且当您拥有很多测试时(如您应有的话),甚至十分之几秒就可以累积到几个小时。 (继续...)

– gbr
17年11月2日在18:03



@Warbo ..也就是说,重要的是它们花费的总时间,因此,如果您有一个很小的项目,而且您确定该项目会很小,那么可以对单个测试的速度更加宽容。而且,如果您真的不希望经常运行它们,则完全可以自由选择,甚至让他们致电OPMROC员工来从柜子中获取并传真文件。您还可以选择在测试很少的情况下放松,然后在测试开始过多时回过头来加快测试速度,但是您必须考虑到这是您积累的债务。

– gbr
17年11月2日在18:03

#4 楼

迈克尔·费瑟斯(Michael Feathers)在他的《有效地使用传统代码工作》一书中说过:


在行业中,人们经常来回讨论特定测试是否为单元测试。 [...]我回到两种品质:测试运行速度快吗?它可以帮助我们快速定位错误吗?


您的测试是否可以帮助快速定位错误并快速运行?如果是,那就去做!如果没有,那就不要!就是这么简单!

这么说,您正在与其他人一起工作的环境中,必须与他们相处。即使您私下不同意,您也可能不得不按照自己的方式做。

评论


这是一个很好的经验法则,但是如果他写了“是否向最常运行的测试套件中添加特定的测试”,而不是弄乱“单元测试”这个术语,他将避免困惑。

– gbr
17-10-31在14:24



@gbr如果测试结果准确,那么唯一重要的事情就是测试运行的速度。如果我有100个测试,每个测试的运行时间都在0.1秒以内,那么总共运行的时间将少于10秒。我很高兴经常运行它们。如果我进行1000次测试,则最多需要1分40秒。太长了,我不会经常运行它们,但是一旦我使用较小的100组测试进行了更改,就将运行它们。我不在乎这在技术上是验收测试还是其他。如果可以帮助我更快地发现错误,则无论语义如何,我都会做到。测试仅在运行时才提供价值。

– CJ Dennis
17年1月1日,下午3:55

我大都同意(例如,独立性是另一个非常重要的事情),但是,如果迈克尔·费瑟斯不对“单元测试”的含义感到困惑,那就更好了。并不是应该为这种混乱负责(在我到目前为止浏览的部分中,他的书非常出色)。无论如何,我只说了一点点。

– gbr
17年11月1日在18:13

@gbr他没有重新定义单元测试,他说那不应该是您包含的标准。您的标准应该是有用的,这就是他的定义。

– CJ Dennis
17年11月1日在21:03

我重新阅读了该部分;我不确定,这对我来说是一个(某种)定义还是只是一个标准,目前还不清楚。但是实际上,“它能帮助我们快速定位错误吗?”可能很好地暗示了他之前所说的关于隔离等的内容。因此,我可能什么都没做(虽然您的回答仍然可能会被误解)

– gbr
17年11月2日在17:06



#5 楼

我有时针对源代码文件,配置文件等编写了类似的测试。我不会称其为单元测试,因为(a)它们正在访问文件系统并且可能不是超快的(b)我不在乎是否在每次签入时都执行它们(相对于每晚CI服务器)。

您可以将它们称为集成测试;当然,它们比单元测试更接近于这种观点。

我对它们的术语是资源测试。恕我直言,如果它们在CI服务器上每晚执行,则完全有道理:成本极低,如果明智地使用,显然会增加价值。一个明智的定义:如果测试正在检查引起问题的问题(例如您提到的编码)。

#6 楼

单元测试就是关于测试方法或代码“单元”的全部。您正在测试软件中最小的逻辑和代码组。

稍​​后,当您将其与其他单元结合在一起时,您将执行集成测试。

我希望您的团队领导鼓励您的主动性,应该提供其他建议。您肯定有一个正确的想法。

您的SQL是代码,就像任何较低级的语言(如C#或Java)一样,应进行测试。验证和确认属于所有测试级别。因此,包含了编码和SET语句,但不一定要进行专门的测试。诸如行尾或封闭之类的常规内容通常只能使用SCM钩子或功能。

最佳实践是进行回归测试,以确保不会再次引入以前的错误。通常,将在错误的任何解决方案旁边创建测试。如果这些错误未在单元/集成或系统级别的回归测试中涵盖,然后重新引入,则是团队问题,过程问题,而不是单个问题。

问题是...语法错误,通常不会对“单元”中缺少的语句或逻辑块进行测试。您正在以不同组合测试单元的输入和输出,测试可能产生的多种可能性。

回到丢失的SET语句-它们有助于告知输入和输出进行测试的多种可能性对于。如果缺少任何选定的SET,您会写哪种测试会失败?

评论


“代码单元”测试是单元测试的一种方法。以我的经验,这会导致脆弱的测试和巨大的膨胀(例如,过多的嘲笑)。 IMHO更好的另一种单元测试方法是“功能单元”。某些功能(例如,“登录时设置cookie”)是否需要一种方法或十二个相互通信的过程并不重要,它仍然是一个单元。

–Warbo
17年11月1日在21:31

@Warbo-我将其称为(而不是)集成测试。单元测试不需要过多或大量的东西。单元测试应该小而快速。实际上,按功能进行测试可以得到您所描述的内容。易碎测试是1.比其应做的更大或更多的工作。 2.高度耦合3.没有单一责任。

–罗斯
17年11月12日在22:22



#7 楼

如果您的文件成为产品的一部分,则其内容必须正确。没有理由您不会对此进行验证。例如,如果您在某个文件夹中需要六个1024x 1024图像,则一定要编写一个单元测试来检查您是否具有正确的图像。

但是您可能不仅拥有文件,还拥有一些读取文件的代码。您可以为该代码编写单元测试。在上面的示例中,读取六张图像之一的函数是否在内存中返回1024 x 1024图像(或应该产生的图像)。

无论如何,它可能不是单元测试,但却是有用的测试。而且,如果您使用允许您执行有用测试的单元测试框架(不是单元测试),为什么不使用单元测试框架呢?

#8 楼

可能是我误解了您的问题,但是对我来说,这听起来像是一个问题,无需通过任何专用测试即可捕获,而只需通过版本控制系统即可捕获。对代码库的任何更改都应在提交前逐个补丁进行审查。在git中执行此操作的一种简单方法是使用

git add -p


添加更改。对于文本文件中的每个更改,工作目录都会询问您是否真的要留着它。例如,这将使您看到这些“初始SET语句”的删除。

如果整个文件的编码发生更改,则可能会有所不同:算法将无法区分新旧文件,因此git add -p根本不会添加任何内容。这将在我提交任何其他命令之前执行的另一命令中可见,即

git status


在这里,您会看到该文件以红色突出显示,表明存在变化。研究为什么这些问题没有纳入git add -p,将很快使问题变得明显。

评论


祈祷告诉,这如何帮助任何未来的开发人员避免完全相同的问题? ...关于自动化测试(对于断言和按合同设计也有效)的事情是,它们是自动化的。

–user88637
17年11月3日在5:24

@vaxquis确实避免了完全相同的问题-尽管有些偶然,但作为工作流的副作用,由于不同的原因,这是一个好主意。我的意思是,这个问题完全可能发生,这表明OP的团队没有很好地利用他们的VCS。 —不反对自动化测试,但是它们的价值在于测试语义属性,这些语义属性可能会因无害更改程序逻辑而中断。并不是要检查源代码更改的所有可能的愚蠢方式。

–leftaround关于
17年11月3日,9:57



根据您的逻辑,我们不需要安全带;我们只需要更加谨慎地驾驶,减少事故的发生...您错过了OP提出的要点-人为错误。没有任何数量的VCS可以保护您免受此侵害。同样,FWIW:如果测试可以自动化,则应该自动化。人永远是工程过程中最薄弱的环节。 git是最好的例子-一个很棒的工具,但是对于凡人来说几乎是无法使用的。

–user88637
17年11月3日在10:11



@vaxquis不,不!安全带类似于有意义的测试:它们会遇到各种可能会偶然发生的情况。对文件编码的测试类似于一个机器人,它将跟踪您并阻止您通过将豆塞满鼻子来窒息自己。

–leftaround关于
17年11月3日在10:23



根据OP,错误格式的文件已经发生过两次,因此显然很可能是偶然发生的。

– gnasher729
17年11月5日在18:59

#9 楼

需要考虑的另一个角度:由于这两个条件是程序运行的要求,因此您是否不应该将逻辑嵌入到执行逻辑附近?我的意思是:您在读取文件和/或验证其内容之前先测试文件的存在,对吗?那么这有何不同?我认为,由于这是代码外部资源,因此应在实际使用之前在运行时对其进行验证。结果:更强大的应用程序,无需编写其他测试。

评论


仅在运行时失败才能使它成为更强大的应用程序?当然,也可以在问题的根源附近进行运行时检查,但是如果您可以在错误导致运行时问题之前检测出错误,那就更好了,您认为吗?您确定您熟悉自动测试吗?

– gbr
17-10-31在14:18

“只有在运行时失败才能使它成为更强大的应用程序?”如果检查失败,则引发异常。在您的测试中,只需检查要测试的代码部分是否返回了预期的结果:这消除了检查另一个失败原因的负担。

– Gianluigi Zane Zanettini博士
17-10-31在15:23

通常,您的策略(几乎)与单元测试和自动化测试无关,这是不同的,具有不同的用途。

– gbr
17年11月1日在16:13

我打算建议这个。该错误是实现细节。我想如果躲闪的编码是在私有字段而不是独立文件中,则响应会大不相同!听起来OP遇到两个问题:资源文件可能编码错误,生产对开发人员的行为有所不同。通过在运行时检查文件,就在使用它之前,我们解决了第二个问题:dev和prod将引发相同的错误。然后,单元测试可以专注于实际功能而不是实现细节。这些“内部”检查将像私有方法一样执行。

–Warbo
17年11月1日在21:42

@ Dr.GianluigiZaneZanettini Bah ...我放弃了...据我所知,充其量,在您的评论“澄清”之后,您的答案充其量是题外话(不是问题的答案),但实际上,就目前而言,这是完全错误的!无需编写其他测试???我没有足够的声誉来拒绝投票,但请考虑一下我是否做到了。而且我认为继续进行对话没有太大价值。

– gbr
17年11月2日在15:18



#10 楼

测试与任何其他代码都是相同的代码,并且如果足够复杂,也可以从...单元测试中受益。将此类前提条件检查直接添加到测试中似乎是最简单的。

大多数测试足够简单,不需要这样做,但是如果某些测试足够复杂,我看不出这些前提条件有什么根本错误条件检查。当然,没有它们,测试也应该失败,但是一个好的单元测试也可以告诉哪个单元失败了。

作为测试的一部分且必须具有一定内容且编码的脚本可能是一个单元。它可能比其余测试具有更多的代码和逻辑。使用这种脚本进行的测试并不是有史以来最好的设计,并且如果可能的话,应该将其重构为更直接的内容(除非这是集成测试)。

评论


作者在任何地方都没有说过SQL脚本是某些测试的一部分,您似乎误解了问题

– gbr
17年2月2日,18:24



很难理解,我假设SQL脚本是测试的一部分。

– h22
17年11月2日在18:33

您的评论“那里很难理解” ...

– gbr
17年2月2日,18:38



很难理解。否决问题。

– h22
17年3月3日在7:10

#11 楼

首先-测试的目的之一是防止代码中再次出现问题-因此,您绝对应该继续编写具有这种性质的测试。

其次-命名很难。是的,这些显然不是“单元测试”,但是它们可能是构建过程中理想且必要的部分,因为它们可以保护您免受明显的错误的侵扰,并且因为它们可以更快地为您提供有关错误的反馈(特别是鉴于您看不到因此,问题实际上是(应该在您的上下文中)更多地关于何时以及如何运行这些测试,而不是它们的运行方式。

过去,我已经广泛使用了这种测试-他们为我们节省了很多痛苦。

评论


如果有人愿意解释不赞成票,我将不胜感激

–墨菲
17年11月4日在15:26

#12 楼

单元测试是关于独立执行一个代码单元,以确认它针对正确的输入产生了正确的结果。隔离应该使被测单元和测试本身都具有可重复性,即不应依赖或引入副作用。

SQL并不是完全可以隔离进行测试的东西,因此对SQL的任何测试都不能并不是完全的单元测试,而且除了SELECT语句外,几乎肯定会有副作用。我们可以称其为集成测试,而不是单元测试。

确保在开发周期中尽早发现可能引入的任何缺陷始终是明智的做法,这样做是有益的因此,以易于识别缺陷根源的方式,可以快速对其进行纠正。

有问题的测试可以更适当地从“单元测试”的主体中移出并放置在其他位置,但是如果它们正在做一些有用的事情,例如防止可能引入的测试,则不应完全删除。可能需要几个小时才能找到的缺陷。