rot解密
A wise coder once said: “When I commit my code, only God and I know what it does. After a while, only God knows.” It’s the basic definition of code rot and applies to pretty much all code that we write.
一位明智的编码员曾经说过:“当我提交代码时,只有上帝和我知道它在做什么。 过了一会儿,只有上帝知道。” 这是代码腐烂的基本定义,几乎适用于我们编写的所有代码。
Sure, the code will still do exactly what it did when you checked it in or when you pushed it to production, but since it works, it becomes something magical: a proven truth, and you don’t touch proven truths. Over time, the code becomes a behemoth that nobody dares to touch, the legacy part of the codebase. It was designed “a long time ago,” it has functioned exactly the same way since its release, and nobody fully comprehends it. If something needs to be changed, you hack around it; you alter the input or the output, but leave the black box intact.
当然,代码仍会完全按照您签入或将其推入生产时的方式进行操作,但是由于它可以正常工作,因此它变得有些神奇:经过验证的事实,并且您不会碰触经过验证的事实。 随着时间的流逝,代码成为代码库的遗留部分,没人敢触摸的庞然大物。 它是“很久以前”设计的,自发布以来,其功能完全相同,没有人完全理解它。 如果需要更改某些内容,您可以解决它; 您更改输入或输出,但保留黑框完整。
Unless you’re working on a fresh project right now, the codebase you work with most likely has such code you take for granted. And the more challenging your current assignment (or fresh project) is, the more likely it will become a legacy part as well on short notice.
除非您现在正在从事一个新项目,否则您所使用的代码库很可能会认为这样的代码是理所当然的。 而且,您当前的任务(或新项目)越有挑战性,就越有可能在短时间内成为遗留部分。
Since it’s infeasible to fully comprehend all problems that your codebases solve, as well as all their solutions, something else needs to be done to prevent code rot. One way or the other: the code needs to be refactorable to live happily ever after. And that’s where unit tests come in; they provide a simple way to write proofs that your code works as intended. And each proof makes that what is proven refactorable. You can change the code that is being proven, and then have the tests prove if the new code still functions the same way.
由于无法完全理解您的代码库解决的所有问题及其所有解决方案,因此需要采取其他措施来防止代码腐烂。 一种或另一种方式:代码需要可重构,以便以后能够幸福地生活。 这就是单元测试的目的。 它们提供了一种简单的方式来编写证明代码可以按预期工作的证明。 而且每个证明都证明了可重构的东西。 您可以更改要验证的代码,然后让测试证明新代码是否仍以相同的方式运行。
While I would love to tell you every little detail I know about unit testing and how to apply it to your code, I reluctantly agreed with my editor that 150,000 words is too long for one article. So I’m sticking with the basics.
尽管我很想告诉您有关单元测试以及如何将其应用于代码的每一个细节,但我还是勉强同意我的编辑的观点,因为一篇文章中15万个单词太长了。 因此,我坚持使用基础知识。
In principle, a unit test is a small piece of code that does 3 things:
原则上,单元测试是执行三件事的一小段代码:
Assemble – sets up an environment similar to what the code would run in during production, but without being dependent on any other components in your code 组装–设置与生产期间将在代码中运行的环境类似的环境,但不依赖于代码中的任何其他组件 Act – executes the piece of code to be proven 行动–执行待验证的代码段 Assert – verifies that the outcome of the execution is what was expected given the circumstances 断言–验证执行结果是否符合特定情况The astute reader will notice that this doesn’t actually test the code for correctness. If there is a bug caused by assumptions in the code, introduced by the same developer writing the unit test, then the bug won’t be found since the test will be written with the same assumptions. Bugs caused by typos will be found by running the tests, similar to as they would be found if the code was executed in a staging environment. So unit testing is a bit of a misleading name: it’s more unit-proving than unit-testing.
精明的读者会注意到,这实际上并未测试代码的正确性。 如果由同一位开发人员在编写单元测试时引入的代码中的假设导致错误,则不会发现该错误,因为将使用相同的假设编写测试。 通过运行测试将发现由错别字引起的错误,类似于在临时环境中执行代码时所发现的错误。 因此,单元测试的名称有点误导性:比单元测试更能证明单元。
The unit to be proven is up to the developer writing the unit test, although the rule of thumb in this is that the unit has to act, that is, it has to have behavior. Simple entity classes which only store values do not need unit tests since there is nothing to prove. You can rely on the programming language to be able to store values in a variable; there’s no point in testing that.
要证明的单元取决于开发人员编写单元测试,尽管经验法则是单元必须采取行动,即必须具有行为能力。 仅存储值的简单实体类不需要进行单元测试,因为没有任何证据可证明。 您可以依靠编程语言来将值存储在变量中。 测试毫无意义。
On a related note, feel free to ignore code coverage numbers. They’re useless metrics. The only thing that matters is whether the acting components, or units, have proof. You can have a whopping 95% code coverage, but if that 5% is all core business logic then you still have the (about-to-be) black boxes waiting to start rotting.
与此相关的是,可以随意忽略代码覆盖率数字。 它们是无用的指标。 唯一重要的是作用部件或单元是否具有证明。 您可以拥有高达95%的代码覆盖率,但是,如果这5%是所有核心业务逻辑,那么您(仍有)(可能)黑盒正等待着开始腐烂。
The important part of the assemble stage is that the environment mimics production but does not use any components/units not under the test. If you need to prove a method that modifies a value that comes from the database, simplify that to just modifying a value. You can mock the database component that would provide the value in production if your code is tightly coupled.
组装阶段的重要部分是环境模仿生产,但不使用未经测试的任何组件/单元。 如果您需要证明一种修改来自数据库的值的方法,请将其简化为仅修改值。 如果代码紧密耦合,则可以模拟将在生产中提供价值的数据库组件。
With a mock component, you design a fake component which, to the component under test, is presented as the real component but will behave in a predictable way. Say the component you are testing gets a list of names from the database through a data layer component and then capitalizes every first character. The mock object mimics the data layer component but does not actually talk to a database. Instead, it contains a static list of first names.
使用模拟组件,您可以设计一个假冒的组件,该组件在被测组件中会显示为真实组件,但行为会以可预测的方式进行。 假设您要测试的组件通过数据层组件从数据库中获取名称列表,然后大写每个第一个字符。 模拟对象模仿数据层组件,但实际上并不与数据库对话。 相反,它包含名字的静态列表。
<?php ... // ** ORIGINAL METHOD **/ public function getCustomerFirstNames(DBLayer $db) { $dbvalues = $db->getQueryResults("SELECT firstname FROM customers"); $firstnames = array(); foreach($dbvalues as $firstname) { $firstnames[] = ucfirst($firstname); } return $firstnames; } ... <?php //** Mock for DB layer **/ class DBLayer { public function getQueryResults($sql) { return array("John", "tim", "bob", "Martin"); } } <?php ... //**PHPUnit testcase **/ public function setUp() { //mock file included instead of real file $this->db = new DBLayer(); } public function testGetcustomerfirstnamesWillReturnFirstnamesModifiedByUcfirst() { // assemble (together with the runonce setUp() method) $obj = new OriginalClass(); $expected = array("John", "Tim", "Bob", "Martin"); // act $results = $obj->getCustomerFirstNames($this->db); // assert $this->assertEquals($expected, $results, "GetCustomerFirstNames did not ucfirst() the list of names correctly"); } ...With this approach, two things have been achieved:
通过这种方法,已经实现了两件事:
The test component does not rely on any other component 测试组件不依赖任何其他组件 The assembled environment in which we run the test is predictable; it will behave exactly the same every time we run the test 我们运行测试的组合环境是可预测的; 每次我们运行测试时,它的行为将完全相同With a suitable environment in place, the test component can be executed. Given that the input is known, as well as what problem the code is supposed to solve and how it is solved, the outcome can easily be derived. And that is exactly what the assertion stage is for. This is the part that actually proves the code.
在适当的环境中,可以执行测试组件。 假定输入是已知的,以及代码应解决的问题以及如何解决代码,则可以轻松得出结果。 这正是断言阶段的目的。 这是实际证明代码的部分。
The assertion is simply stating: “given known input x, processed by known function f, the output is f(x)”. The developer who wrote function f and the accompanying unit test wrote this function to solve the particular problem at hand. The test removes the need to know or even comprehend the problem, or why it was solved in a particular manner. The code’s functionality was accepted (and hopefully is continuously being accepted by acceptance tests), so as long as the unit test passes; the code is proven correct.
该断言仅说明:“给定已知输入x,由已知函数f处理,输出为f(x)”。 编写函数f和附带的单元测试的开发人员编写了此函数来解决当前的特定问题。 该测试无需了解或理解问题,也无需了解为什么以特定方式解决了问题。 只要单元测试通过,代码的功能就可以被接受(并有望被接受测试不断接受)。 该代码被证明是正确的。
This means that the code can now be refactored as much as needed with the unit test serving as proof that functionality remained unchanged. This counteracts the biggest cause of code rot as the code is now safe to be modified. It can’t be broken as long as it’s being covered by unit tests which pass. If a test fails, the developer is quickly notified and can adjust or revert the applied change, even before the code is executed in a staging environment.
这意味着现在可以使用单元测试根据需要重构代码,以证明功能保持不变。 由于现在可以安全地修改代码,因此可以消除导致代码腐烂的最大原因。 只要它被通过的单元测试覆盖,它就不会被破坏。 如果测试失败,则即使在暂存环境中执行代码之前,开发人员也会Swift收到通知,并可以调整或还原应用的更改。
Now that you have an understanding of what unit tests are and how they work, I’d like to touch on a few points that will make your unit testing experience a whole lot better.
既然您已经了解了什么是单元测试以及它们如何工作,那么我想谈谈几点,这将使您的单元测试体验更好。
Unit tests are not integration tests, acceptance tests, or any other form of implementation tests.While every unit testing framework will give you a full toolkit making it very tempting to sneak in some integration or acceptance tests… don’t. Unit tests should only prove that the solution of the original developer is still being applied despite any changes to the code under test. While you can write integration or acceptance tests with the same framework, you may want to consider using two separate frameworks so it’s easier to keep the two separate.
单元测试不是集成测试,验收测试或任何其他形式的实施测试。 尽管每个单元测试框架都将为您提供完整的工具包,但非常诱人地进行一些集成或验收测试……但并非如此。 单元测试应仅证明即使对被测代码进行了任何更改,原始开发人员的解决方案仍在使用。 虽然您可以使用同一框架编写集成或验收测试,但您可能需要考虑使用两个单独的框架,这样可以更轻松地将两个框架分开。
Don’t forget to write integration/acceptance tests as well.A unit test proves a tiny piece of code in isolation, but it does not prove that the system works. This is covered by integration and acceptance tests; a test suite which is slow running, combining all components, and proves that the pieces work together as they should. A unit test is concise and fast, allowing it to be ran before and after every change without costing you much time. An integration/acceptance test is something you run at the end of a development cycle, during the QA phase.
不要忘记也写集成/验收测试。 单元测试可以孤立地证明一小段代码,但不能证明系统可以工作。 这由集成和验收测试涵盖; 一个运行缓慢的测试套件,结合了所有组件,并证明了各个部分可以按预期工作。 单元测试简洁快速,可以在每次更改之前和之后运行,而无需花费大量时间。 集成/验收测试是在QA阶段的开发周期结束时运行的。
Keep your unit tests simple and concise.Keep in mind that unit tests will be marked as failed when at least one assertion fails, so a general rule of thumb worth following is the fewer assertions in a test, the better. A good test never mixes behavior proofs. If such a test fails, you immediately know exactly what functionality is broken. If behavior proofs get mixed or multiple assertions are present in the same test, you’ll need to start up a debugger just to figure out what behavior is now broken. All assertions could be failing, or just one, only the first failed assertion will be reported.
保持单元测试简单明了。 请记住,当至少一个断言失败时,单元测试将被标记为失败,因此值得遵循的一般经验法则是,测试中的断言越少越好。 好的测试永远不要混淆行为证明。 如果此类测试失败,您将立即确切知道哪些功能已损坏。 如果行为证明混杂在一起,或者同一测试中存在多个断言,则需要启动调试器,以弄清楚现在是什么行为被破坏了。 所有断言都可能失败,或者只有一个,将仅报告第一个失败的断言。
Name your unit tests verbosely.The test method name will never be called manually, so there’s no need to use clever or concise naming conventions. Verbosity in the name serves as an extra form of documentation for what is being tested.
详细命名您的单元测试。 测试方法名称永远不会手动调用,因此无需使用巧妙或简洁的命名约定。 名称中的详尽程度可以作为正在测试的文档的额外形式。
An added bonus is that when output in TestDox format is chosen, a unit testing framework like PHPUnit produces a nice checklist with human readable lines displayed that serves as documentation. If you write your tests before the actual implementation, you can even use this output as a quick checklist to see what’s finished and what still needs to be written.
另外一个好处是,当选择了TestDox格式的输出时,像PHPUnit这样的单元测试框架会生成一个不错的清单,并显示人类可读的行作为文档 。 如果您在实际实现之前编写测试,甚至可以将此输出用作快速清单,以查看完成了哪些内容以及仍需要编写什么内容。
Only write unit tests for code you own.While unit testing frameworks give you so many tools that it becomes tempting to write tests for everything in sight, only code that you’ve written personally can be properly unit tested. Good code is a solution for a problem; it’s the result of thoroughly understanding the problem, an understanding of the possible solutions, and finally the actual implementation of a solution. If only the implementation is visible, the test would be written without the same assumptions and understanding of the original problem solver. The unit test is then very likely to miss specific design choices, making the test unreliable.
只为您拥有的代码编写单元测试。 虽然单元测试框架为您提供了许多工具,但诱使您为可见的一切编写测试变得很诱人,但只有您亲自编写的代码才能进行正确的单元测试。 好的代码是解决问题的方法。 这是彻底了解问题,了解可能的解决方案以及最终解决方案的实际结果的结果。 如果只有实现是可见的,则编写测试时将没有相同的假设和对原始问题解决者的理解。 这样,单元测试很可能会错过特定的设计选择,从而使测试不可靠。
Write unit tests for bug fixes.Before fixing the bug, write a test that proves the current code is wrong (because the assertion fails). Then fix the code, causing the unchanged assertion to pass. As a side effect, it’s great way to start adding unit tests to a currently uncovered piece of code. This helps cures code rot, one little unit test at a time, as that part of the code is now proven to be correct (for the current definition of correct). This also prevents the bug from being re-introduced when refactoring, since allowing the buggy behavior again would cause the unit test to fail.
编写单元测试以修复错误。 在修复该错误之前,编写一个测试来证明当前代码是错误的(因为断言失败)。 然后修复代码,使不变的断言通过。 副作用是,这是开始将单元测试添加到当前未发现的代码段的好方法。 这有助于解决代码腐烂问题,一次只需要进行一次小单元测试,因为现在已证明该部分代码是正确的(对于当前的正确定义)。 这也可以防止在重构时重新引入错误,因为再次允许错误行为会导致单元测试失败。
Never change the code under test.When you start writing tests for code already written, you will quickly notice that most code is hard to test because of tight coupling or other design choices. It will be very tempting to adjust the code under test a little to make it way easier to write a unit test for it, but don’t do this. The whole purpose of unit testing is to prove the existing code so it is safe to refactor it. Refactoring before writing the unit test is exactly the risk you want to avoid – you won’t know what you will break or which bugs you will introduce.
切勿更改被测代码。 当您开始为已经编写的代码编写测试时,您会很快注意到,由于紧密的耦合或其他设计选择,大多数代码难以测试。 稍微调整被测代码以使其更容易编写单元测试非常诱人,但不要这样做。 单元测试的全部目的是证明现有代码,因此可以安全地对其进行重构。 编写单元测试之前进行重构正是您要避免的风险–您将不知道会破坏什么或将引入哪些错误。
Managers aren’t going to assign a full team to each problem and have them maintain that tiny codebase for all of eternity, so if you want to prevent your genius solutions from rotting away, you are writing your own little virtual helpers in the form of unit tests to maintain that codebase for you.
经理们不会为每个问题分配一个完整的团队,而是让他们在整个永恒的过程中都维持这个很小的代码库,因此,如果您想防止天才的解决方案腐烂,您可以编写自己的小虚拟助手,形式为单元测试可以为您维护该代码库。
If you’re proud of the code you write, you should have unit tests for it. If you don’t know how to write unit tests, then you should be learning how to write them. If your team doesn’t use unit tests, convince them that it’s a required tool.
如果您以编写的代码感到自豪,则应该对其进行单元测试。 如果您不知道如何编写单元测试,则应该学习如何编写它们。 如果您的团队不使用单元测试,请说服他们这是必需的工具。
There is so much more to be learned about unit tests and why you should write them; for further reading visit:
关于单元测试以及为什么要编写它们,还有很多要学习的知识。 进一步阅读访问:
Getting Started with PHPUnit
PHPUnit入门
Getting Started with Unit Testing in PHP (using Simpletest and PHPUnit via Composer)
PHP单元测试入门(通过Composer使用Simpletest和PHPUnit)
What My Co-workers and I Learned When Trying to Write Unit Tests for PHPUnit
我和我的同事在尝试为PHPUnit编写单元测试时学到了什么
PHPUnit Manual: Writing Tests for PHPUnit
PHPUnit手册:为PHPUnit编写测试
Feel free to ask around your local PHP User Group, or attend sessions about the subject at your next conference, as well. Most professional PHP developers have at least some experience with PHPUnit and unit testing in general.
随时随地询问您当地PHP用户组,或者在您的下一次会议上参加有关该主题的会议。 一般而言,大多数专业PHP开发人员至少对PHPUnit和单元测试有一定的经验。
A wiser coder would say: “When I commit my code, only God, the unit tests and I know what it does. After a while, only God and the unit tests know.”
明智的编码员会说:“当我提交代码时,只有上帝才能进行单元测试,而且我知道代码的作用。 过了一会儿,只有上帝和单元测试才知道。”
Image via Fotolia
图片来自Fotolia
翻译自: https://www.sitepoint.com/preventing-code-rot-101-unit-testing/
rot解密
相关资源:jdk-8u281-windows-x64.exe