我是一个相对较新的软件开发人员,我认为我应该改进的一件事是测试自己的代码的能力。每当我开发一项新功能时,我都会发现很难遵循所有可能的途径,以便发现错误。我倾向于遵循一切正常的道路。我知道这是程序员遇到的一个众所周知的问题,但是我们现任雇主没有测试人员,而我的同事们似乎在这方面做得很好。
在我的组织中,我们既不进行测试驱动的开发,也不进行单元测试。这将对我有很大帮助,但这不太可能会改变。
你们认为我可以做些什么来克服这个问题?测试自己的代码时使用什么方法?
我是一个相对较新的软件开发人员,我认为我应该改进的一件事是测试自己的代码的能力。每当我开发一项新功能时,我都会发现很难遵循所有可能的途径,以便发现错误。我倾向于遵循一切正常的道路。我知道这是程序员遇到的一个众所周知的问题,但是我们现任雇主没有测试人员,而我的同事们似乎在这方面做得很好。
在我的组织中,我们既不进行测试驱动的开发,也不进行单元测试。这将对我有很大帮助,但这不太可能会改变。
你们认为我可以做些什么来克服这个问题?测试自己的代码时使用什么方法?
Answers:
编码员的工作是建造东西。
测试员的工作是破坏事物。
最困难的是破坏刚构建的东西。只有克服这种心理障碍,您才能成功。
弗朗索索,根据您的意见,我将在此处做出一些假设:
“我们既不进行TDD也不进行单元测试。这将对我有很大帮助,但这种情况不太可能改变。”
由此看来,我怀疑您的团队没有在测试上投入太多价值,或者管理层不会为团队花费时间来整理现有代码并使技术负担降至最低。
首先,您需要说服您的团队/管理层进行测试的价值。保持外交。如果管理层使您的团队不断前进,则需要向他们展示一些事实,例如每个版本的缺陷率。花费在修复缺陷上的时间可以更好地花在其他方面,例如改进应用程序并使它更适应未来的需求。
如果团队和管理层总体上对修改代码无动于衷,并且对此感到不满意,则可能需要寻找另一个工作地点,除非您可以像我所说的那样说服他们。在我工作过的所有地方,我都不同程度地遇到了这个问题。从缺少适当的域模型到团队沟通不畅,都是如此。
关心代码和所开发产品的质量是一个很好的属性,并且您一直希望在其他人中得到鼓励。
如果您使用C,Objective-C或C ++进行编码,则可以使用CLang Static Analyzer批判您的源代码,而无需实际运行它。
有一些可用的内存调试工具:ValGrind,Mac OS X上的Guard Malloc,* NIX上的Electric Fence。
一些开发环境提供了使用调试内存分配器的选项,该分配器执行诸如用垃圾填充新分配的页面和新释放的页面,检测未分配的指针的释放以及在每个堆块之前和之后写入一些数据的事情,其中调试器是如果该数据的已知模式发生变化,则调用。
Slashdot上的一些人说,他从调试器中单步执行的全新源代码行中获得了很多价值。“就是这样。”他说。我并不总是听从他的建议,但是当我得到他的建议时,对我很有帮助。即使您没有激发异常代码路径的测试用例,也可以在调试器中旋转一个变量以采用此类路径,例如通过分配一些内存,然后使用调试器将新指针设置为NULL而不是内存地址,然后逐步完成分配失败处理程序。
使用断言-C,C ++和Objective-C中的assert()宏。如果您的语言不提供断言功能,请自己编写。
自由使用断言,然后将其保留在代码中。我将assert()称为“不断测试的测试”。我最常使用它们在大多数函数的入口处检查前提条件。这是“按合同编程”的一部分,它是Eiffel编程语言中内置的。另一部分是后置条件,即在函数返回点使用assert(),但是我发现我没有得到比前提条件更多的里程。
您还可以使用assert来检查类不变式。虽然没有严格要求所有类具有任何不变式,但是最明智地设计的类确实具有它们。类不变式是某些条件,该条件总是正确的,而不是在成员函数内部,这可能会使您的对象暂时处于不一致状态。此类函数始终必须在返回之前恢复一致性。
因此,每个成员函数都可以在进入和退出时检查不变量,并且该类可以定义一个称为CheckInvariant的函数,任何其他代码都可以在任何时间调用。
使用代码覆盖率工具检查源中的哪些行实际受到测试,然后设计能够激发未经测试的行的测试。例如,您可以通过在配置了很少物理内存,没有交换文件或很小的虚拟机的VM中运行应用程序来检查低内存处理程序。
(出于某种原因,我从来都不愿意使用,尽管BeOS可以在没有交换文件的情况下运行,但那样的话非常不稳定。编写BFS文件系统的Dominic Giampaolo敦促我不要在没有交换的情况下运行BeOS。我没有看看为什么这很重要,但这一定是某种实现工件。)
您还应该测试代码对I / O错误的响应。尝试将所有文件存储在网络共享上,然后在应用程序工作量很大时断开网络电缆的连接。同样,如果通过网络进行通信,则断开电缆连接-或关闭无线设备。
我发现特别令人生气的一件事是没有健壮的Javascript代码的网站。Facebook的页面加载了数十个小的Javascript文件,但是如果其中任何一个未能下载,整个页面就会中断。只能通过某种方式提供某种容错功能(例如通过重试下载),或者在某些脚本没有下载时提供某种合理的备用。
在编写大型重要文件的过程中,尝试使用调试器或* NIX上的“ kill -9”杀死您的应用程序。如果您的应用程序具有良好的结构,则整个文件将被写入或根本不会被写入,或者如果仅部分写入,则写入的内容将不会被破坏,保存的数据将完全可用重新读取文件后的应用程序。
数据库始终具有容错磁盘I / O,但几乎没有其他任何类型的应用程序具有。日记文件系统可以防止在电源故障或崩溃时文件系统损坏,但它们根本不会采取任何措施来防止最终用户数据损坏或丢失。这是用户应用程序的责任,但除数据库之外几乎没有其他任何方式可以实现容错。
当我考虑测试代码时,通常会经历一系列思考过程:
我发现最简单的方法是开发测试以及代码。一旦编写了代码片段,我就喜欢为其编写测试。在编写了几千行具有非平凡的循环代码复杂性的代码之后,尝试进行所有测试是一场噩梦。在添加几行代码之后再添加一两个测试确实很容易。
顺便说一句,除非您所在的公司和/或您的同事没有进行单元测试或TDD,但这并不意味着您不能尝试它们,除非特别禁止。也许使用它们来创建健壮的代码将是其他人的一个很好的例子。
除了其他答案中给出的建议外,我建议使用静态分析工具(维基百科提供了多种针对各种语言的静态分析工具的列表),以便在测试开始之前发现潜在的缺陷并监视与之相关的某些指标代码的可测试性,例如圈复杂度,Halstead复杂度度量以及内聚和耦合(您可以使用扇入和扇出测量它们)。
找到难以测试的代码并使其更易于测试将使您更容易编写测试用例。另外,及早发现缺陷将为整个质量保证实践(包括测试)增加价值。从这里开始,熟悉单元测试工具和模拟工具将使您更轻松地实施测试。
但我们目前的雇主没有测试员,而我的同事似乎在这方面做得很好
您的同事必须真正出色,不遵循TDD或单元测试,并且永远不会产生错误,因此在某种程度上,我怀疑他们自己不会执行任何单元测试。
我猜想您的同事正在进行的测试多于允许进行的测试,但是由于管理层不了解这一事实,因此组织遭受了损失,因为管理层感觉到没有在进行真正的测试并且错误数量很少,因此测试并不重要,因此不会安排时间。
与您的同事交谈,并尝试弄清楚他们正在执行哪种单元测试并进行模拟。以后,您可以为单元测试和TDD属性提供更好的原型方法,然后将这些概念缓慢地引入团队,以便于采用。
黑匣子测试!您应该在考虑测试的情况下创建类/方法。您的测试应基于软件的规范,并应在序列图上明确定义(通过用例)。
现在,由于您可能不想进行测试驱动的开发...
对所有传入数据进行输入验证;不要相信任何人。.net框架基于无效参数,空引用和无效状态引发了许多异常。您应该已经在考虑在UI层上使用输入验证,因此这与中间件中的技巧相同。
但是您确实应该进行某种自动化测试。这些东西可以挽救生命。
在我的经验中
测试单元,如果不是全自动的,那就没用了。这更像是一个尖头毛老板可以买的东西。为什么?,因为Test Unit承诺您可以节省一些自动化测试过程的时间和金钱。但是,某些测试单元工具却相反,它迫使程序员以某种怪异的方式工作,并迫使其他人创建过度扩展的测试。在大多数情况下,这不会节省工作时间,但会增加从质量检查到开发人员的时间。
UML浪费时间。一支白板+笔可以完成相同,更便宜,更快速的操作。
顺便说一句,如何擅长编码(并避免错误)?
a)原子性。一个函数可以完成一个简单的任务(或几个单独的任务)。因为它易于理解,所以易于跟踪和解决。
b)同源性。例如,如果您使用存储过程调用数据库,则对其余代码进行处理。
c)识别,减少和隔离“创意代码”。大多数代码几乎都是复制和粘贴。相反,创意代码是新代码,它充当原型,可能会失败。该代码容易产生逻辑错误,因此减少,隔离和识别它很重要。
d)“薄冰”代码是您知道它“不正确”(或潜在危险)但仍需要的代码,例如用于多任务处理的不安全代码。尽量避免。
e)避免使用黑匣子代码,这包括您未完成的代码(例如框架)和正则表达式。用这种代码很容易错过一个错误。例如,我在一个使用Jboss的项目中工作,发现Jboss中没有一个错误,而是10个错误(使用最新的稳定版本),找到这些错误是PITA。避免特别使用Hibernate,因为它会隐藏实现,因此会导致bug。
f)在代码中添加注释。
g)用户输入作为错误的来源。识别它。例如,SQL注入是由用户输入引起的。
h)识别团队的不良要素,并分离分配的任务。一些程序员倾向于拧紧代码。
i)避免不必要的代码。例如,如果该类需要Interface,则使用它,否则避免添加不相关的代码。
a)和b)是关键。例如,我的系统出现问题,当我单击按钮(保存)时,它没有保存表单。然后我做了一个清单:
和一个旁注
大多数时候,QA很糟糕(作为Dev的单独部分),如果他们不在项目中工作,那将毫无用处。他们进行一些通用测试,仅此而已。他们无法识别大多数逻辑错误。以我为例,我在一家著名的银行工作,一名程序员完成了代码,然后将其发送给质量检查人员。质量检查人员批准了该代码并将其投入生产...然后,该代码失败了(史诗般的失败),您知道是谁的责任吗?是的,程序员。
测试人员和程序员从不同的角度面对问题,但是两个角色都应充分测试功能并查找错误。角色不同之处是重点。传统的测试人员只能从外部(即黑匣子)看到应用程序。他们是应用程序功能要求方面的专家。程序员应该既是功能要求又是代码方面的专家(但往往会更专注于代码)。
(这取决于组织是否明确要求程序员是需求专家。无论如何,隐含的期望在那里-如果您设计错了,您(而不是需求人员)会受到指责。)
这种双重专家的角色使程序员感到费解,除了最有经验的人之外,这可能会降低要求的熟练程度。我发现我必须在思维上换档,以考虑该应用程序的用户。这对我有帮助:
我认为您想在两个方面开展工作。一种是政治的,使您的组织在某种程度上采用测试(希望随着时间的推移,他们将采用更多的测试)。与您工作场所外的质量检查工程师联系。查找质量检查书籍清单。在相关的维基百科文章中查找。熟悉质量检查原则和惯例。学习这些内容将使您准备好在组织中提出最有说服力的案例。好的质量检查部门确实存在,并且确实为组织增加了可观的价值。
作为个人开发人员,请采用可用于您自己工作中的策略。通过共同开发代码和测试自己使用TDD。保持测试清晰并保持良好状态。如果被问到为什么要这样做,可以说您正在防止回归,这可以使您的思维过程更好地组织起来(两者都是正确的)。编写可测试的代码是一门艺术,要学习它。对您的开发人员来说是一个很好的例子。
在某种程度上,我在这里向自己宣讲,因为我做这些东西的方式比我应该知道的要少。