简体   繁体   English

是否值得尝试为世界上最紧密耦合的网站编写测试?

[英]Is it worth trying to write tests for the most tightly coupled site in the world?

Imagine that 90% of your job is merely to triage issues on a very massive, very broken website. 想象一下,90%的工作仅仅是为了在一个非常庞大,非常破碎的网站上分类问题。 Imagine that this website is written in the most tightly coupled, least cohesive PHP code you've ever seen, the type of code that would add the original developers to your "slap on sight" list. 想象一下,这个网站是用你所见过的最紧密耦合,最不具有凝聚力的PHP代码编写的,这种代码类型可以将原始开发人员添加到你的“即时视线”列表中。 Imagine that this web application is made up of 4 very disparate parts (1 commercial, 2 "repurposed", and 1 custom) and a crap-ton of virtual duct tape and shims. 想象一下,这个Web应用程序由4个非常不同的部分组成(1个商业,2个“重新定位”,1个自定义)和一堆垃圾虚拟胶带和垫片。 Imagine that it contains the type of programming practices in which major components of the website actually rely on things NOT working properly, and fixing these broken things usually breaks other things. 想象一下,它包含了一种编程实践,其中网站的主要组件实际上依赖于不能正常工作的东西,修复这些破碎的东西通常会破坏其他东西。 Imagine that you know from too many bad experiences that changing one seemingly innocuous part of the website, such as splitting a "name" field into two separate "first" and "last" fields, will bring the site to its knees and require hours of rollbacks, merges and patches. 想象一下,你从太多不好的经历中知道,改变一个看似无害的网站部分,比如将“名称”字段分成两个独立的“第一”和“最后”字段,将使网站瘫痪,需要数小时回滚,合并和补丁。 Imagine pleading with the customer for years to just ditch the code and start all over but being met with Enterprise-Grade despair and hand wringing. 想象一下,多年来恳求客户抛弃代码并重新开始,但遭遇企业级绝望和手工绞尽脑汁。 Then imagine getting ASAP/EMERGENCY tickets to implement new features that in any other web site would take 4 hours but you know better with this site so you quote 40 hours, then blow right by that and bill 80 hours, but it's OK because the client is used to that with their website. 然后想象获得ASAP / EMERGENCY门票以实现新功能,在任何其他网站上需要4个小时,但你对这个网站有更好的了解,所以你引用了40个小时,然后直接吹了80个小时,但它没关系,因为客户端用于他们的网站。

Here are some other things that you should also imagine: 以下是您还应该想象的其他一些事情:

  • there are no tests at all right now 现在没有任何测试
  • there are googleteen different layers of logins. googleteen有不同的登录层。 Some customers actually have 3 different accounts for different sections of the website 有些客户实际上有3个不同的帐户用于网站的不同部分
  • when I say "tightly coupled", I mean the loops of include/require statements would probably map out like a celtic knot 当我说“紧耦合”时,我的意思是include / require语句的循环可能会像凯尔特结一样映射出来
  • when I say "least cohesive" I mean some stuff is organized sort of like MVC, but it's not really MVC. 当我说“最不具凝聚力”时,我的意思是某些东西有点像MVC,但它不是真正的MVC。 In some cases it may take you several hours just to find out how URI A is mapped to file B 在某些情况下,您可能需要几个小时才能找到URI A如何映射到文件B.
  • the UI was written like "obtrusive" and "inaccessible" were the buzzwords of the day 用户界面写得像“突兀”,“无法访问”是当时的流行语

Imagining all that, is it even worth trying to achieve even a moderate level of test coverage? 想象一下,甚至值得尝试达到中等水平的测试覆盖率? Or should you, in this imaginary scenario, just keep doing the best you can with what you've been given and hoping, praying, maybe even sacrificing, that the client will agree to a rewrite one of these days and THEN you can start writing tests? 或者,在这个想象的情景中,你应该继续尽你最大的努力,你已经给予了什么,并希望,祈祷,甚至牺牲,客户将同意重写其中一天,然后你就可以开始写作了测试?

ADDENDUM 附录

Since many of you brought it up: I have approached the possibility of a re-write at every chance I've had to date. 因为你们中的许多人提出了这个问题:我已经接触过每次机会重新写作的可能性。 The marketing people I work with know that their code is crap, and they know it's the fault of the "lowest bid" firm they went with originally. 与我合作的营销人员知道他们的代码是垃圾,他们知道这是他们最初使用的“最低出价”公司的错。 I've probably overstepped my bounds as a contractor by pointing out that they spend a crap ton of money on me to provide hospice care for this site, and that by redeveloping it from scratch they would see an ROI very quickly. 作为一名承包商,我可能已经超越了我的界限,指出他们花了一大笔钱给我这个网站提供临终关怀服务,而且从头开始重新开发他们会很快看到投资回报率。 I've also said that I refuse to rewrite the site as-is, because it doesn't really do what they want it to do anyway. 我还说我拒绝按原样重写网站,因为它实际上并没有按照他们的意愿去做。 The plan is to rewrite it BDD style, but getting all the key players in one place is tough, and I'm still not sure they know what they need. 计划是重写它的BDD风格,但让所有关键球员都在一个地方很难,我仍然不确定他们知道他们需要什么。 In any case, I fully expect that to be A Very Big Project. 无论如何,我完全希望这是一个非常大的项目。

Thanks for all the feedback so far! 感谢到目前为止的所有反馈!

You'll likely not get full coverage for some time. 一段时间后,您可能无法完全覆盖。 But you can write tests for new code/features you implement. 但是您可以为您实现的新代码/功能编写测试。 Start small. 从小处开始。 Don't try to do everything all at once. 不要试图一次完成所有事情。

And perhaps the book " Working effectively with Legacy Code " is worth a read? 或许“ 有效地使用遗留代码 ”这本书值得一读?

Edit 编辑

I also would recommend watching this presentation from Uncle Bob which touches on this scenario and how to transform a bad code base, into a good one using "Progressive Widening" 我还建议观看Bob叔叔的演讲,该演讲涉及这个场景,以及如何使用“Progressive Widening”将糟糕的代码库转换为好的代码库。

Edit 2 编辑2

Start by finding any self contained functions. 首先找到任何自包含的函数。 Functions that don't reference anything but the arguments passed in. Move and organize them into helper classes. 除了传入的参数之外不引用任何内容的函数。将它们移动并组织成辅助类。 This is likely just temporary, as many will end up in different classes later, but this will help identify some duplicated code, and start getting things organized. 这可能只是暂时的,因为许多人最终会在不同的类中结束,但这将有助于识别一些重复的代码,并开始组织事情。 Then, look at how these are used, write tests based on these uses. 然后,看看如何使用它们,根据这些用途编写测试。 And pat yourself on the back. 并拍拍自己的背部。 You've now started making your code maintainable. 您现在已经开始使代码可维护。

Edit 3 编辑3

With great timing InfoQ just posted another article How To Do Large Scale Refactoring which is specifically this sort of thing, and another, older article called Refactor or Rewrite? 有了很好的时机InfoQ刚发布了另一篇文章如何进行大规模重构 ,这是一种特别的事情,另一篇较旧的文章称为Refactor或Rewrite? and there are techniques like the Mikado Method where you have to realize you can't always make the move you want in one step, you have to make other moves to setup and realize your goal. 还有像Mikado方法这样的技术,你必须意识到你不能总是在一步中完成你想要的移动,你必须做出其他动作来设置并实现你的目标。

Absolutely not. 绝对不。

If you say that multiple things rely on other things specifically not working then how can you even begin to test it? 如果你说多件事依赖其他特别不起作用的东西那你怎么能开始测试呢?

Personally I would say scrap it and start over. 我个人会说废弃它并重新开始。 Four hour features that take 80? 四小时功能需要80? I hope this is an exaggeration. 我希望这是夸大其词。 The headaches you must have. 你必须要头痛。

I would start with a very firm proposal to re-write the code base. 我会从一个非常坚定的建议开始重新编写代码库。 Hand-wringing clients must be told the blunt truth some times. 有时候必须把令人烦恼的客户告诉他们。 How many other developers will work with a broken base? 有多少其他开发人员将使用破碎的基础? Make some pretty cost / benefit charts. 制作一些漂亮的成本/效益图表。

By all means write tests for code you write . 通过各种方式为编写的代码编写测试 Don't neglect that. 不要忽视这一点。 I'm saying I wouldn't try to write tests on the existing code base. 我说我不会尝试在现有代码库上编写测试。

Give it a go 搏一搏

Writing tests enables you to refactor. 编写测试可以让你重构。 If you write your tests at a high-enough level, you might manage to make it so you can refactor without having to re-write the tests every time. 如果你在足够高的水平上编写测试,你可能会设法做到这一点,这样你就可以重构,而不必每次都重新编写测试。

It's a least worth a go, on a small part of the site (I know you won't be able to fully isolate any part, but you can still target part). 在网站的一小部分(我知道你不能完全隔离任何部分,但你仍然可以锁定部分)是最不值得的。

Maybe set yourself a time budget (it's down to you to work out what's affordable/worth it), then have a go with some tests and some refactorings. 也许为自己设定一个时间预算 (由你来决定什么是实惠的/值得的),然后进行一些测试和一些重构。 If it doesn't work out, roll back. 如果不能解决问题,请回滚。 If it does, carry on. 如果是,继续。

Good luck! 祝好运!

First, if your customer is use to your estimates being half what it actually takes, fix your estimates! 首先,如果您的客户使用的估算值是实际需要的一半,请修正您的估算值! It is nice the customer is 'OK' with the estimates being off -- but it is critical you get your estimates more in line with effort actually needed. 很高兴客户对“估计已关闭”感到满意 - 但重要的是让您的估算更符合实际需要的工作量。 Without that, what customer would ever consent to a major refactoring -- let alone a rewrite. 没有它,客户会同意进行重大重构 - 更不用说重写了。 So get some history of being right with estimates, then move to rework the project. 因此,通过估算得到一些正确的历史,然后转向重新设计项目。

As for writing tests. 至于写测试。 That is even more vital for what you describe than for a green-field project. 对于你描述的内容而言,这比绿地项目更为重要。 In every piece of code you touch ask yourself if it is possible to decouple the behavior that should be there from the behavior that is there. 在每一段代码中,您都会问自己是否可以将应该存在的行为与那里的行为分开。 Write the code the way it should be (with tests) and then add a layer of abstraction to make it the way it currently is (and test that too!). 以应有的方式编写代码(使用测试),然后添加一个抽象层,使其成为当前的样式(并进行测试!)。 You will feel like your adding to the mess, and you will be -- but slowly, over time, the tests will give you confidence in these areas. 你会觉得你加入了这个烂摊子,你会 - 但慢慢地,随着时间的推移,测试会让你对这些方面充满信心。

If it's anything like what I've been dealing with, it will be on the order of pulling a single method out into a helper class and patching it back into the existing code, hardly seems worth it -- but it does pay off every time you have to touch that part of the system again. 如果它就像我一直在处理的那样,那就是把一个方法拉到一个帮助类并将它修补回现有代码的顺序,几乎不值得 - 但它确实每次都能得到回报你必须再次触摸系统的那一部分。 Like they say -- "leave it better than you found it" and you'll start finding it in better shape each time you come back to it. 就像他们说的那样 - “让它比你发现它更好”,每次你回到它时你都会开始找到更好的形状。 Tests are the best way to leave it better than you found it. 测试是让它比你发现它更好的最佳方法。

But seriously, getting the client confident in the accuracy of your estimates is required before they will be fully confident in your ability to handle a rework. 但严重的是,要让客户对您的估算准确性充满信心,他们才能对您处理返工的能力充满信心。

Absolutely write tests. 绝对写测试。 Especially in a tight-coupled environment the tests are going to be even more critical (since a bug fix in one area can drastically affect other areas due to the tight coupling). 特别是在紧耦合环境中,测试将变得更加重要(因为由于紧耦合,一个区域中的错误修复会严重影响其他区域)。

Now, realize that this will likely not be a trivial task. 现在,意识到这可能不是一项微不足道的任务。 In order to write tests, you'll need to modify the code to be testable. 为了编写测试,您需要修改代码以使其可测试。 In order to modify the code, you need to have tests. 要修改代码,您需要进行测试。 So you're caught in a dependency cycle... 所以你陷入了依赖循环......

However, look at the potential benefits. 但是,看看潜在的好处。 That should tell you if it is really worth it or not. 这应该告诉你它是否真的值得。

If you do start out, start small. 如果你真的开始,那就从小做起。 Pick one tiny piece that looks loosely-coupled, and test that. 挑选一块看起来松散耦合的小块,并测试一下。 Then find something else that's not that tangled. 然后找一些不那么纠结的东西。 Test all the loosest pieces first (the low hanging fruit). 首先测试所有最松散的碎片(低垂的果实)。 Then, once you get to the really tight parts, you'll both feel more comfortable and (hopefully) have more insight as to what you really need to do. 然后,一旦你到达非常紧凑的部分,你会感觉更舒服,并且(希望)对你真正需要做的事情有更多的了解。

Remember, you don't need 100% coverage to reap the benefits. 请记住,您不需要100%的覆盖率来获得收益。 Each test adds meaning... 每个测试都增加意义......

You can't scrap it. 你不能废弃它。 The customer isn't going to let you, and it might not be the best path anyways. 客户不会让你,它可能不是最好的路径。

So instead of quoting 40 hours for a fix that should have taken minutes... quote 60. The customer seems A-OK with that. 因此,而不是引用40小时进行应该花费几分钟的修复...引用60.客户似乎A-OK。 Use 40 to fix, and 20 to refactor... and write tests on what you refactor. 使用40来修复,20来重构......并对你重构的内容进行测试。 If the 60 runs to 100, then spend 120; 如果60跑到100,那么花120; 80 to fix, and 40 to refactor/test. 80修复,40重构/测试。

Build in time to improve the thing into your normal estimates, or find new work; 及时建立以将事物改进到正常估计中,或找到新工作; the current situation, it sounds like, will drive you into hating our field. 听起来,现在的情况会让你讨厌我们的领域。

This sounds like in order to make it testable at all , you'd have to rewrite parts of the system from scratch - unavoidably causing tons of bugs in the process. 这听起来像,以使其可测试的所有,你就必须重写了整个系统的部件-不可避免地造成错误吨的过程中。

From what you describe, the old system is not worth putting that kind of effort into. 根据您的描述,旧系统不值得付出这样的努力。

I would under no circumstances try and introduce testing for this, but try to get permission to rewrite as soon as possible. 在任何情况下我都不会尝试为此进行测试,但尝试尽快获得重写权限。

If your client doesn't see the light, consider whether refactoring the project is worth giving some time of your own: Working with clean code is so much better for one's well-being... 如果您的客户看不到光,那么考虑重构项目是否值得给自己一些时间:使用干净的代码对于一个人的幸福来说更好......

The most important thing (After buying Working efficiently with legacy code ) is to start small. 最重要的事情(购买后使用遗留代码高效工作 )是从小做起。 I work on several projects, each several thousand PHP lines long and often without a single function (and don't even think of objects) and whenever i have to change code i try to refactor the part into a function and write a test for it. 我在几个项目上工作,每个项目长达数千个,并且通常没有单个函数(甚至没有想到对象),每当我必须更改代码时,我会尝试将该部分重构为函数并为其编写测试。 This is combined with extensive manual testing of that part so i can be sure it works as before. 这与该部件的广泛手动测试相结合,因此我可以确定它像以前一样工作。 When i have multiple functions for similar things i move them as static methods into a class and then, step by step, replace them with proper object-oriented code. 当我有类似事物的多个函数时,我将它们作为静态方法移动到一个类中,然后,一步一步地用适当的面向对象的代码替换它们。

Every step from moving it into a function to changing it into a real class is surrounded by unit testing (not very good one as 90% of the code are SQL queries and it's nearly impossible to set up a reliable testing database, but i can still test the behaviour). 从将其转换为函数到将其更改为真实类的每一步都被单元测试所包围(不是非常好的,因为90%的代码是SQL查询,并且几乎不可能建立可靠的测试数据库,但我仍然可以测试行为)。

Since a lot of code repeats (i found a single SQL query repeated 13 times in a single file and many times more in the other 50 files of that project) i could change all other places, but i don't since those are neither tested nor can i be sure the surrounding code doesn't depend on that code in some wierd way (think global ). 由于大量代码重复(我发现单个SQL查询在一个文件中重复13次,而在该项目的其他50个文件中重复多次)我可以更改所有其他地方,但我不会因为那些都没有经过测试我也无法确定周围的代码是否以某种奇怪的方式依赖于该代码(想想global )。 That code can be changed as soon as i have to touch that code anyways. 只要我必须触摸该代码,就可以更改该代码。

It's a long and tedious work and every time i see the code i feel a step (or rather a leap) closer to mental breakdown, but the code quality will improve (slowly but mostly reliably). 这是一项漫长而乏味的工作,每当我看到代码时,我感觉更接近精神崩溃的一步(或更确切地说是跳跃),但代码质量将会提高(缓慢但最可靠)。

Your situation seems to be quite similar, so maybe my ideas might help you with your code. 您的情况似乎非常相似,所以也许我的想法可能会帮助您使用代码。

In short 简而言之

Start small, change only what you work on and begin to write only limited unit tests and expand them the more you learn about the system. 从小处开始,仅更改您的工作并开始只编写有限的单元测试,并在您了解系统的情况下扩展它们。

Start by doing black box, functional testing, connected parts or bits and pieces here and there. 首先要做黑盒子,功能测试,连接部件或零碎件。 This makes continued development and refactoring/rewriting much easier. 这使得持续开发和重构/重写变得更加容易。

Been there, doing that. 在那里,做到这一点。

Took a while till we could start adding unit testing, but got there eventually. 花了一段时间,直到我们可以开始添加单元测试,但最终到达那里。

It's still far from bulletproof but all developers are much more confident to dare to change/fix things when you know that there is a test suite waiting to try to verify your code changes. 它仍然远非防弹,但当您知道有一个测试套件等待尝试验证您的代码更改时,所有开发人员都更有信心敢于更改/修复问题。

From your scenario, you should have a long list of fragile areas of the code that tend to be affected by innocuous changes (or at least areas that absolutely must work). 从您的场景中,您应该有一长串代码的脆弱区域,这些区域往往会受到无害的更改(或至少绝对必须工作的区域)的影响。 If you can wright tests against this list, you have a quick way to find out when a change you're implementing has broken something. 如果您可以根据此列表进行测试,您可以快速找到实施更改的时间。

In theory, definitely. 理论上,绝对。 The more tightly coupled, bug ridden the maintenance process then the more important the tests. 绑定越紧密,错误就越多,维护过程就会越多,测试就越重要。 In practise, walk away and live another day! 在实践中,走开,再过一天!

If things behave reliably, you can test them, right? 如果事情表现得可靠,你可以测试它们,对吧? Your system works the majority of the time, so you can test for those success conditions. 您的系统大部分时间都在工作,因此您可以测试这些成功条件。

..innocuous part of the website, such as splitting a "name" field into two separate "first" and "last" fields, will bring the site to its knees and require hours of rollbacks ..网站的一部分,例如将“名称”字段拆分为两个单独的“第一”和“最后”字段,将使网站陷入困境并需要数小时的回滚

Splitting a field apart such as first and last name sounds like a potential massive thing - but sounds like you've learned your lesson. 拆分一个字段,如名字和姓氏听起来像一个潜在的巨大的东西 - 但听起来你已经吸取了教训。 At least try to get some funding for a full size test system and put the procedures in place to make moving production data to it automatic, so you can fully test this thing. 至少尝试为全尺寸测试系统获得一些资金并制定程序以自动生成生产数据,以便您可以对此进行全面测试。

Sounds pretty horrible though. 听起来很可怕。 Time to dust of the ole resume? 时间尘埃的简历?

You might want to consider billing another 40 hours/iteration to create a nice BDD (domain) model of how the application works or better: should work. 您可能需要考虑另外40小时/迭代计费,以创建应用程序如何工作或更好的良好BDD(域)模型:应该工作。 That creates a nice framework where you can document the needed features. 这创建了一个很好的框架,您可以在其中记录所需的功能。 When the model is complete enough, you can estimate how much time you'd need to convert it to a working application. 当模型足够完整时,您可以估计将其转换为工作应用程序所需的时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM