測試驅動開發 測試前移
by Ronauli Silva
通過羅納利·席爾瓦(Ronauli Silva)
I first read about test driven development (TDD) in some technical reviews blog, but I barely read it (or thought about it). Why would people write tests first when they already knew the logic?
我首先在一些技術評論博客中閱讀了有關測試驅動開發(TDD)的信息,但幾乎沒有閱讀(或考慮過)。 人們為什么已經知道邏輯就為什么要先寫測試?
So what was this all about? Writing tests first, incrementally building the logic, and doing it in iterations. The funny thing is, when you give two programmers five minutes to code a simple fibonacci sequence and ask one to do TDD, by the end of the 5 minutes, the programmer doing TDD may say “I have test for it!” But they won’t have finished the code. On the other hand, the other one will have finished the entire fibonacci sequence and will have optimized it.
那到底是什么呢? 首先編寫測試,逐步構建邏輯,然后進行迭代。 有趣的是,當您給兩個程序員五分鐘的時間來編寫一個簡單的斐波那契序列并要求一個人進行TDD時,在5分鐘結束時,執行TDD的程序員可能會說:“我已經對此進行了測試!” 但是他們不會完成代碼。 另一方面,另一個將完成整個斐波那契數列并對其進行優化。
為什么要使用TDD? 單元測試不夠好嗎? (Why use TDD? Aren’t unit tests good enough?)
At the end of last year, I finally met TDD face to face. In a three-month bootcamp session, we were forced to always do things with TDD. I was already struggling enough, and so my brain always rebelled when it came time to write the tests.
去年年底,我終于與TDD面對面了。 在為期三個月的訓練營會議中,我們被迫始終使用TDD進行操作。 我已經很掙扎了,所以到了編寫測試的時候,我的大腦總是反叛。
Why should we write tests first when I can directly code the logic, my brain asked? Can’t we just write them later? After all functionality is finished?
我的大腦問,當我可以直接編寫邏輯代碼時,為什么要先編寫測試? 我們不能稍后再寫嗎? 所有功能完成之后?
Let me give you a quick overview of TDD in a nutshell.
讓我簡要概述一下TDD。
Let’s say I’m creating a fibonacci function. I might ask, what is the simplest assertion on a fibonacci? => Returns 1 if input is 1.
假設我要創建一個斐波那契函數。 我可能會問,關于斐波那契的最簡單的斷言是什么? =>如果輸入為1,則返回1。
What is the simplest solution for that assertion? The simplest solution, I mean it.
該斷言最簡單的解決方案是什么? 最簡單的解決方案 我是認真的。
Now, next move. What is next simplest assertion for fibonacci?=> Returns 2 for inputs = 3
現在,下一步。 斐波那契的下一個最簡單的斷言是什么?=>輸入2返回2 = 3
Again, let’s fix this very quickly. Just return it and add some branching.
同樣,讓我們??快速修復此問題。 只需返回它并添加一些分支即可。
Move to another expectation. Aim for a bigger number. Do it iteratively, incrementally.
移到另一個期望。 爭取更大的數量。 逐步進行迭代。
On and on it goes, until you get the nice solution for your fibonacci function. If you want to practice more, try adding memoization during the process (and don’t forget—with TDD).
不斷進行下去,直到您獲得斐波那契函數的理想解決方案為止。 如果您想練習更多,請嘗試在此過程中添加備忘(不要忘了-使用TDD)。
Did you notice what we did there? The baby steps, your assertion, and how we define the solution? Your thought process got separated into these five critical points:
您注意到我們在那里做了什么嗎? 嬰兒的腳步,您的主張以及我們如何定義解決方案? 您的思考過程分為以下五個關鍵點:
Simple & Incremental Design — You have to think about what is the simplest thing a particular function could do, and what’s coming next. The fibonacci example describes this point perfectly.
簡單和增量設計-您必須考慮某個特定功能可以做的最簡單的事情,以及下一步的工作。 斐波那契示例很好地描述了這一點。
Assertion — What is your expectation of that function? And how do you describe that expectation? Will other people understand it quickly?Some test libraries provide you with a test description feature. That string is the only verbose thing that explains what your code is doing.
斷言-您對該功能有什么期望? 您如何描述這種期望? 其他人會很快理解嗎?某些測試庫為您提供測試描述功能。 該字符串是解釋您的代碼正在執行的唯一冗長的操作。
Make sure it’s a good explanation, or you’ll get a call on your holiday because your unreadable test case is failing, and no one knows why.
確保這是一個很好的解釋,否則您的假期會打來電話,因為您無法讀取的測試用例失敗了,沒人知道原因。
Testable Design — How should you design it so it can be testable? Take a look at these two snippets below.
可測試的設計 -應該如何設計它以便可測試? 看看下面的這兩個片段。
The first one:
第一個:
By doing TDD, since you write the test first, you have to make sure that your code is testable. You can see from the example that you don’t even test your fibonacci function. Instead, you test the side-effect of that fibonacci logic in your code, which invokes the console.log function.
通過執行TDD,由于您首先編寫了測試,因此必須確保您的代碼是可測試的 。 從示例中可以看到,您甚至沒有測試斐波那契函數。 而是在代碼中測試該斐波那契邏輯的副作用 ,該邏輯調用console.log函數。
The other thing is, you never know which one is failing, the console.log() or your fibonacci block when you refactor it. In this way, TDD leads us to increase modularity in our code.
另一件事是,當您重構它時,您永遠都不知道哪個是失敗的console.log()或您的fibonacci塊。 通過這種方式,TDD使我們提高了代碼的模塊化程度。
Now, let’s look at second snippet.
現在,讓我們看看第二個片段。
In the second example, we can see that we test the particular fibonacci function, not the other function that spikes on it. We are confident that the function works perfectly under the conditions that we state. We are sure that if the other function invokes our fibonacci and fails, it is not from our code.
在第二個示例中,我們可以看到我們測試了特定的斐波那契函數,而不是其他尖峰函數。 我們相信該功能在我們聲明的條件下可以完美運行。 我們確定如果另一個函數調用我們的斐波那契并失敗,則不是來自我們的代碼。
Negatives and Corner Cases — what do you expect when something’s not right: is it invoked with null? Does it throw an exception? How should it be handled? What could possibly happen in the code? What could be the strangest and weirdest thing that could happen in this loop? What test can catch that?
否定情況和極端情況 –如果出現不正確的情況,您會期望什么:使用null調用嗎? 它會引發異常嗎? 應該如何處理? 代碼中可能會發生什么? 在此循環中可能發生的最奇怪和最奇怪的事情是什么? 有什么考驗可以抓住?
Boundaries — Should you expect that from your function? Are you sure it’s not another class’s responsibility?
邊界 -您是否應該從職能中獲得期望? 您確定這不是另一堂課的責任嗎?
我對TDD的問題 (My issues with TDD)
Yes, it is slow indeed. Sometimes, your time is doubled since you’re writing both tests and logic at the same time. This makes how you use your keyboard important (typing speed, better shortcut usage, and so on).
是的,確實很慢。 有時,由于您同時編寫測試和邏輯,因此您的時間增加了一倍。 這使您使用鍵盤的方式變得很重要(鍵入速度,更好的快捷方式用法等)。
And even worse —when the requirements change—you have to refactor or delete and rewrite test code you worked hard on. Which means that tests code is code you write that is more likely to be deleted in the future. And you are doing it, iteratively. DELETES. CODES. REWRITES. AGAIN. IN A LOOP!
更糟糕的是,當需求發生變化時,您必須重構或刪除并重寫您一直在努力的測試代碼。 這意味著測試代碼是您編寫的代碼,將來很有可能被刪除。 而您正在迭代地這樣做。 刪除。 編碼。 重寫。 再次。 一圈!
Think about it. Why would you write code that is more likely to be deleted?
想一想。 您為什么要編寫更有可能被刪除的代碼?
“Nope, that’s enough of this TDD thing. I’ll do it when I find a strong reason why I should spend time writing code I’m likely to delete”, I said to myself.
“不,這個TDD夠了。 我對自己說:“當我發現有充分的理由為什么我應該花時間編寫可能會刪除的代碼時,我會做的。”
And that was right before I unconsciously started digging my own grave.
那是在我不知不覺開始挖掘自己的墳墓之前。
為什么我改變主意 (Why I changed my mind)
The enlightenment came about two months later, when I was assigned to a group that did not implement TDD well at all.
大約兩個月后,當我被分配到一個完全沒有很好地執行TDD的小組時,就得到了啟迪。
I mean, they implemented TDD, but they left the tests broken. They didn’t bother to fix those failing test cases (which often broke because the requirements had changed). And this happened because of the most cliche reason in the world: they didn’t have time. They had to make deadlines.
我的意思是,他們實施了TDD, 但是卻使測試失敗了。 他們沒有費心去修復那些失敗的測試用例(由于需求發生了變化,測試用例經常失敗)。 發生這種情況是由于世界上最陳詞濫調的原因:他們沒有時間。 他們必須規定最后期限。
After looking at the situation, I mumbled “Look, see! This TDD doesn’t work in the production world!” It made me question many things: is this TDD worth fighting for? Is TDD worth the time? Does it even deliver any business value?
看完情況后,我喃喃地說:“看,看! 該TDD在生產環境中不起作用!” 這讓我提出了很多問題:這個TDD是否值得爭取? TDD值得嗎? 它甚至提供任何商業價值嗎?
After a while, I realized that the problems were growing exponentially, tasks were getting delayed, chaos was reigning, and the developer experience was getting really bad — all because they implemented TDD poorly and halfheartedly. It was even worse than not writing tests at all.
過了一會兒,我意識到問題正在成倍增長,任務被延遲,混亂不斷,開發人員的經驗真的變得很糟糕-所有這些都是因為他們對TDD的執行不力而全力以赴。 比根本不編寫測試還要糟糕。
Here are some of the issues it caused:
這是它引起的一些問題:
- When I added a new feature or refactored things, I didn’t know whether that code was failing or not because the test was already failing. 當我添加新功能或重構事物時,我不知道該代碼是否失敗,因為測試已經失敗。
We were forced to have high threshold on code coverage. And make no mistake, programmers are smart and sneaky. They write tests with no expectations, like smoke tests. And that was the only test they had on that particular logic. It was like, we only knew it was failing after everything was on fire. How dangerous.
我們被迫對代碼覆蓋率設置高閾值。 毫無疑問,程序員是聰明而狡猾的 。 他們編寫沒有期望的測試,例如冒煙測試。 那是他們對該特定邏輯進行的唯一測試。 就像,我們只知道一切都著火之后才失敗。 多么危險
- We used CI/CD for deployment. And we always deployed even though it was failing, which was scary: You never knew whether your production itself was failing, or if it was because you didn’t fix the tests. 我們使用CI / CD進行部署。 而且,即使失敗了,我們也總是進行部署,這很可怕:您永遠不知道自己的生產是否失敗,或者是因為您沒有修復測試。
- After production, we ended up fixing strange and completely out-of-mind bugs. We had never even thought of those strange conditions before. (Ever find a situation when something in a try-catch block is failing but not throwing an exception?) 生產后,我們最終修復了奇怪且完全過時的錯誤。 我們以前從未想過那些奇怪的情況。 (是否找到了try-catch塊中的某項失敗但沒有引發異常的情況?)
Oh, the horror!
哦,恐怖!
After analyzing the situation, doing it in iterations, and reflecting on it, I realized that TDD is actually a golden nugget. If done right, it can make us better developers.
在分析了情況,反復進行并反思之后,我意識到TDD實際上是一個金塊。 如果做得對,它可以使我們成為更好的開發人員。
為什么我現在愛TDD (Why I now love TDD)
使用TDD,您的bug更少 (With TDD, you have fewer bugs)
You’ll hardly miss things that you can catch with your tests.
您幾乎不會錯過可以通過測試掌握的東西 。
When you get a requirement, you write a test for it first. Then you run the test, and see if it fails first. When you add the logic, you see if it passes.
收到需求后,您首先要為其編寫測試。 然后運行測試,然后查看它是否首先失敗。 添加邏輯時,您可以查看它是否通過。
Seeing it fail is important, because you know what broke your code. In the long run, this practice ensures that all lines in your code are well-tested.
看到它失敗很重要,因為您知道什么破壞了您的代碼 。 從長遠來看,這種做法可以確保代碼中的所有行都經過良好測試。
TDD節省了很多時間(將來) (TDD saves you lot of time (in the future))
CI/CD relies heavily on tests. If you write the wrong tests (or too few tests) you already wasted five hours to find what errors it couldn’t catch. If you write good tests, and spend just five more minutes writing deeper and more complete conditions of your code, you’ll save time debugging it in the future.
CI / CD嚴重依賴測試。 如果您編寫了錯誤的測試(或測試太少),您已經浪費了五個小時來查找無法捕獲的錯誤。 如果您編寫了良好的測試,并且只花了五分鐘以上的時間來編寫更深入,更完整的代碼條件,則可以節省將來調試它的時間。
TDD處理編碼的人為方面 (TDD deals with the human aspects of coding)
The main ones being negligence and forgetfulness. If you write all the logic directly, by the end of, say, line 190, you may forget why you multiplied a variable by 100 at line 19.
主要是疏忽和健忘。 如果直接編寫所有邏輯,例如在第190行的末尾,您可能會忘記為什么在第19行將變量乘以100。
But, by doing it incrementally and stating the assertion of our code, we gradually build our understanding. This makes us understand the code and its behaviors better.
但是,通過逐步執行并聲明代碼聲明,我們逐漸建立了理解。 這使我們更好地理解了代碼及其行為。
As a bonus, we have sort of living and functional documentation of our code. You can see which test is failing if you delete the previous line, and you instantly know why.
值得一提的是,我們還提供了一些有效的代碼文檔。 如果刪除上一行,您可以看到哪個測試失敗,并且您立即知道原因。
TDD可幫助您集中精力 (TDD helps you focus)
Programmers tend to write too much code, or write code that does too much. Or they try to plan for conditions that never exist. Often, when my team practiced pair pairing, I discovered that TDD allowed us to write less code compared to other teams that didn’t do TDD. While coding, we were focused on getting the test case passed — nothing less, nothing more.
程序員傾向于寫太多的代碼,或者寫太多的代碼。 或者他們試圖為不存在的情況做計劃。 通常,當我的團隊練習配對時,我發現與沒有TDD的其他團隊相比,TDD允許我們編寫更少的代碼。 在編碼時,我們專注于通過測試用例-沒什么,僅此而已。
TDD也有益于您的大腦 (TDD also benefits your brain)
You have proof of your code’s readiness for production, even before deploying it. You don’t have to worry about things you already tested for before. You don’t have to brag to your project manager about how project is going, because you can show them that the tests are passing!
您甚至可以在部署代碼之前就證明您的代碼已經可以投入生產。 您不必擔心之前已經測試過的東西。 您不必向項目經理吹噓項目的進展情況,因為您可以向他們證明測試已通過!
However, TDD is not always your silver bullet. It takes time. You have to set up the project — such as the environment, mocks, and stubs — even before you start doing anything.
但是,TDD并不總是 你的銀彈。 這需要時間。 您必須在開始做任何事情之前就設置項目(例如環境,模擬和存根)。
But remember, time spent on writing tests is not wasted time. It’s the time you invest now to save your time later. It’s the investment you make on the system you build, as you build code on top of more code. And you want to make its foundation as solid as possible. TDD gives you that.
但是請記住,花在編寫測試上的時間不會浪費時間。 現在是時候進行投資,以節省以后的時間了。 當您在更多代碼之上構建代碼時,這就是您對所構建系統的投資。 您想使其基礎盡可能牢固。 TDD為您提供。
In the end, it could cost you a fortune if you don’t do TDD. It may take time, but it is good for you and your team in the long run.
最后,如果您不進行TDD,可能會花費您一筆巨款。 這可能會花費一些時間,但從長遠來看,這對您和您的團隊都是有好處的。
翻譯自: https://www.freecodecamp.org/news/test-driven-development-i-hated-it-now-i-cant-live-without-it-4a10b7ce7ed6/
測試驅動開發 測試前移