軟件測試測試用例編寫_不要先編寫所有軟件測試-只需編寫一個

軟件測試測試用例編寫

Test Driven Development (TDD) is sometimes described as “writing tests first”. The TDD mantra states that we should not write code before we have written automated tests that exercise that code. Writing code first is considered suboptimal.

測試驅動開發(TDD)有時被稱為“首先編寫測試”。 TDD的口頭禪規定,在編寫用于執行該代碼的自動測試之前,請勿編寫代碼。 首先編寫代碼被認為是次優的。

And of course, writing code first is how we develop software following the so-called waterfall model. In that model, we divide software development activities into stages. For example, we have a ‘requirements gathering’ stage, we have an ‘application building’ stage, we have an ‘application testing’ stage, we have an ‘application deployment’ stage and so on.

當然,首先編寫代碼是我們遵循所謂的瀑布模型開發軟件的方式。 在該模型中,我們將軟件開發活動劃分為多個階段。 例如,我們有一個“需求收集”階段,有一個“應用程序構建”階段,有一個“應用程序測試”階段,有一個“應用程序部署”階段,依此類推。

But how is that different from the agile methodology? Don’t we have the exact same stages in agile?

但是,這與敏捷方法學有何不同? 我們在敏捷中沒有完全相同的階段嗎?

敏捷方法論與瀑布方法論 (Agile Methodology vs Waterfall Methodology)

Of course we do. The crucial difference is that in agile, those stages are not gated.

當然可以 關鍵的區別在于,在敏捷開發中,這些階段不會受到限制。

In waterfall, we gate the stages and execute them in strict sequence. This means we won’t begin building the shipping application until such time as the requirements have been gathered, completed, signed off on and frozen. Once requirements are frozen (and controlled by our change management policies), we move into the next stage (or phase) – application building.

在瀑布中,我們對階段進行門控并嚴格執行。 這意味著直到需求被收集,完成,簽署并凍結后,我們才會開始構建運輸應用程序。 一旦需求被凍結(并由我們的變更管理策略控制),我們便進入下一階段(或階段)–應用程序構建。

And similarly, we won’t move into the testing stage until the entire application has been built and we have reached the code complete milestone, at which point code changes have been frozen.

同樣,在整個應用程序都已構建并且達到代碼完整的里程碑之前,我們不會進入測試階段,此時代碼更改已被凍結。

Once code gets frozen (and code freeze is then controlled by our change management policies), we hand it off to the testers. The testing phase begins, and only once all testing has completed (and provided that no significant defects have been detected), do we move into the deployment phase.

一旦代碼被凍結(然后凍結由我們的變更管理策略控制),我們便將其交給測試人員。 測試階段開始,只有完成所有測試(并且前提是未檢測到重大缺陷)后,我們才進入部署階段。

In agile, we do all the above activities in parallel. At the same time. We keep working on user stories (specs) while simultaneously building the shipping application. As we’re building the application we are also testing it. And as we are building and testing the application, we are also deploying it.

在敏捷中,我們同時進行上述所有活動。 與此同時。 我們會繼續處理用戶案例(規格),同時構建運輸應用程序。 在構建應用程序時,我們也在對其進行測試。 在構建和測試應用程序時,我們也在部署它。

We learn from the shipping application deployed to production and use that validated learning as the feedback that will inform new user stories. That way, the loop gets closed, and we’re iterating, improving the value incrementally.

我們從部署到生產中的運輸應用程序中學習,并將經過驗證的學習用作將為新用戶故事提供信息的反饋。 這樣,循環就閉合了,我們進行迭代,逐步提高了價值。

The only way to enable such iterative value stream delivery is by relying on automated tests. And as we’ve described, those tests are being written very early in the game. Actually, tests must be written before we write shipping code.

實現這種迭代式價值流交付的唯一方法是依靠自動化測試。 正如我們已經描述的那樣,這些測試是在游戲的早期階段編寫的。 實際上,在編寫運輸代碼之前必須先編寫測試。

Why then is the title of this article “Don’t Write All Your Tests First, Just Write One”? It sounds a bit confusing. Let’s unpack the meaning of this title. But first, here's an overview of what tech we'll be using:

那么,為什么標題為“不要先編寫所有測試,只編寫一個”? 聽起來有點混亂。 讓我們解開此標題的含義。 但是首先,這是我們將使用的技術的概述:

本練習使用的技術棧 (The technology stack used for this exercise)

In the attempt to keep the exercise simple and easy to follow, I have chosen .NET Core platform, together with xUnit.net testing platform. To follow the coding examples, please install .NET Core and xUnit.net.

為了使練習簡單易行,我選擇了.NET Core平臺以及xUnit.net測試平臺。 要遵循編碼示例,請安裝.NET CorexUnit.net

In order to be able to run the sample code, please open ./tests/tests.csproj file and add this line to the ItemGroup:

為了能夠運行示例代碼,請打開./tests/tests.csproj文件并將此行添加到ItemGroup

<ProjectReference Include="../app/app.csproj" />

You’re now all set for following the coding exercises.

現在您已經準備好進行編碼練習。

一個簡單的例子 (A simple example)

To understand the difference between writing all tests first and writing one test first, it may be better to show rather than just tell.

要了解先編寫所有測試與先編寫一個測試之間的區別,最好顯示而不是僅僅講。

So let’s try to build a simple example – for this exercise I’ve chosen a trivial case of calculating a tip at a restaurant. Often times we find ourselves in a position where we want to tip the restaurant for the service, but it’s tough to calculate percentages in our head. So a nifty little Tip Calculator could come in handy.

因此,讓我們嘗試建立一個簡單的示例-在本練習中,我選擇了一個在餐廳計算小費的簡單案例。 通常,我們發現自己想要為餐廳提供服務小費,但是很難計算出我們所占的百分比。 因此,一個漂亮的小Tip Calculator可能會派上用場。

Here are the expectations:

這是期望值:

As a patronI want to calculate the total bill (total plus the tip)Because I want to compliment the restaurant for the service

作為顧客我想計算總賬單(總計加上小費),因為我想對餐廳的服務表示贊賞

方案1:贊助人計算可怕服務的總數 (Scenario 1: Patron calculates the total for terrible service)

Given that the restaurant total is $100.00And the service was terribleWhen the tip calculator calculates the total chargeThen tip calculator shows $100.00 total charge

假設餐廳的總價為$ 100.00并且服務很糟糕,當小費計算器計算總費用時,小費計算器將顯示$ 100.00的總費用

方案2:顧客計算服務質量差的總數 (Scenario 2: Patron calculates the total for poor service)

Given that the restaurant total is $100.00And the service was poorWhen the tip calculator calculates the total chargeThen tip calculator shows $105.00 total charge

假設餐廳的總價為$ 100.00并且服務很差,當小費計算器計算總費用時,小費計算器將顯示$ 105.00的總費用

方案3:顧客計算出良好服務的總金額 (Scenario 3: Patron calculates the total for good service)

Given that the restaurant total is $100.00And the service was goodWhen the tip calculator calculates the total chargeThen tip calculator shows $110.00 total charge

假設餐廳的總價為$ 100.00并且服務很好,則當小費計算器計算總費用時,小費計算器將顯示$ 110.00的總費用

方案4:顧客計算出優質服務的總額 (Scenario 4: Patron calculates the total for great service)

Given that the restaurant total is $100.00And the service was greatWhen the tip calculator calculates the total chargeThen tip calculator shows $115.00 total charge

假設餐廳的總價為$ 100.00,服務很棒,那么當小費計算器計算總費用時,小費計算器就會顯示$ 115.00的總費用

方案5:顧客計算出優質服務的總額 (Scenario 5: Patron calculates the total for excellent service)

Given that the restaurant total is $100.00And the service was excellentWhen the tip calculator calculates the total chargeThen tip calculator shows $120.00 total charge

假設餐廳的總價為$ 100.00,服務非常好,當小費計算器計算總費用時,小費計算器顯示總費用為$ 120.00

Let’s now implement the above user story.

現在,讓我們實現上面的用戶故事。

We see that the story has 5 acceptance criteria (a.k.a. scenarios). Now we move into the analysis phase – think about what should be the first functionality that our Tip Calculator application should implement. But first, let’s open the command line terminal and create the new directory:

我們看到這個故事有5個接受標準(也稱為方案)。 現在,我們進入分析階段-考慮一下Tip Calculator應用程序應該實現的第一個功能。 但是首先,讓我們打開命令行終端并創建新目錄:

md TipCalculator
cd TipCalculator

and create app and tests directories inside the TipCalculator directory.

并在TipCalculator目錄中創建apptests目錄。

Now cd tests and run:

現在進行cd tests并運行:

dotnet new xunit

Then cd .. and cd app, then run:

然后cd ..cd app ,然后運行:

dotnet new classlib

We’re now ready to boogie!

我們現在可以開始聊天了!

Open your favourite text editor (mine is Visual Studio Code) and set your mind on the expectations. What behaviour are we expecting from the Tip Calculator?

打開您最喜歡的文本編輯器(我的是Visual Studio Code ),然后放下期望。 我們期望Tip Calculator有什么行為?

To narrow the scope of our expectations, it usually helps to take one acceptance criteria (i.e. one scenario) and focus on it first. Let’s take scenario #1:

為了縮小我們的期望范圍,通常有助于采用一個接受標準(即一種情況)并首先關注它。 讓我們來看場景1:

方案1:贊助人計算可怕服務的總數 (Scenario 1: Patron calculates the total for terrible service)

Given that the restaurant total is $100.00And the service was terribleWhen the tip calculator calculates the total chargeThen tip calculator shows $100.00 total charge

假設餐廳的總價為$ 100.00并且服務很糟糕,當小費計算器計算總費用時,小費計算器顯示總費用為$ 100.00

In case the service was terrible, we are not adding any tips, and Tip Calculator is calculating a $0.00 tip. So how do we automate that scenario?

萬一服務糟糕,我們不會添加任何小費, Tip Calculator會計算$ 0.00小費。 那么,如何使這種情況自動化?

My first expectation would be that we need to somehow inform the Tip Calculator that the service was terrible. We either type the word ‘Terrible’ into the input field, or we select ‘Terrible’ from the list of available service ratings.

我的第一個期望是,我們需要以某種方式告知Tip Calculator該服務很糟糕。 我們可以在輸入字段中輸入單詞“ Terrible”,或者從可用服務等級列表中選擇“ Terrible”。

So the first thing to do here is to articulate some expectations regarding Tip Calculator’s ability to get notified that the service was terrible.

因此,這里要做的第一件事是闡明有關Tip Calculator能夠獲得通知該服務很糟糕的能力的一些期望。

I like to always start with the expectation that what the user inputs is valid. So I’d first write a test that checks if the rating ‘Terrible’ is recognized by the Tip Calculator as a valid rating.

我總是希望用戶輸入的內容有效。 因此,我首先要編寫一個測試,檢查“ Tip Calculator是否將“可怕”等級識別為有效等級。

Go to the tests directory, rename the autogenerated UnitTest1.cs file to TipCalculatorTests.cs and add the following test:

轉到tests目錄,將自動生成的UnitTest1.cs文件重命名為TipCalculatorTests.cs并添加以下測試:

[Fact]
public void CheckIfRatingTerribleIsValid(){	var expectedResponseForValidRating = true;	var actualResponseForValidRating = false;	Assert.Equal(expectedResponseForValidRating, actualResponseForValidRating);
}

Now go to the command line, cd tests, and run:

現在轉到命令行cd tests并運行:

dotnet test

Of course, the above trivial test will fail, because we have hardcoded the values. But it’s always a good practice to make sure we see our tests fail before we proceed. Not observing a test fail may give us false sense of safety later on, if no tests fail and we end up thinking that everything works as expected.

當然,上述微不足道的測試將失敗,因為我們已經對值進行了硬編碼。 但是,在繼續進行之前,確保我們看到測試失敗是一種很好的做法。 如果沒有測試失敗,則不遵守測試失敗可能會在以后給我們帶來錯誤的安全感,而我們最終認為一切都會按預期進行。

A few more observations about the above test:

關于上述測試的更多觀察結果:

  • It helps if the test name is descriptive. I chose CheckIfRatingTerribleIsValid to communicate the fact that we must make sure our application is capable of recognizing our commands.

    如果測試名稱具有描述性,它將很有幫助。 我選擇CheckIfRatingTerribleIsValid來傳達這樣一個事實,即我們必須確保我們的應用程序能夠識別我們的命令。

  • It also helps if the expected and actual variable names are descriptive. I chose expectedResponseForValidRating and actualResponseForValidRating as fairly indicative of what our expectation in this test is, and also what actual value will the Tip Calculator produce.

    如果期望的變量名和實際的變量名是描述性的,這也有幫助。 我選擇expectedResponseForValidRatingactualResponseForValidRating得到公平意味著就是我們在本次測試的期望是,也什么實際價值,并將在Tip Calculator產品。

  • Test is a first-class source code and must be approached with equal care lavished upon the production code.

    測試是一流的源代碼,必須以與生產代碼相當的謹慎對待。

最初的設計決定 (First design decision)

At this point, we are forced to make a decision – how will our nascent Tip Calculator know if the service rating provided by the user is valid or not?

在這一點上,我們不得不做出決定-我們新生的Tip Calculator如何知道用戶提供的服務等級是否有效?

The design decision that comes to mind is that Tip Calculator must be able to store and retrieve some data. In this case, the data we’re interested in is the service rating.

想到的設計決定是Tip Calculator必須能夠存儲和檢索一些數據。 在這種情況下,我們感興趣的數據就是服務等級。

If we go back to the user story and review the five acceptance criteria, we will see that the expectations are that Tip Calculator must be able to recognize five different service ratings:

如果我們回到用戶故事并回顧五個接受標準,我們將看到, Tip Calculator必須能夠識別五個不同的服務等級:

  1. Terrible

    可怕
  2. Poor

    較差的
  3. Good

  4. Great

  5. Excellent

    優秀的

So the simplest way to get Tip Calculator to store that information would be to endow it with an array, or a list.

因此,使“ Tip Calculator存儲該信息的最簡單方法是為其賦予數組或列表。

But rather than rushing in to implement that list, we should examine the expectations again, to see if there’s anything else we may have missed. And there is – not only must Tip Calculator be able to recognize valid service ratings, it also must be able to associate each rating with a percentage value.

但是,我們不要急于執行該列表,而應該再次檢查期望,以查看是否還有其他我們可能錯過的事情。 并且–不僅Tip Calculator必須能夠識別有效的服務等級,還必須能夠將每個等級與百分比值相關聯。

Our analysis shows the following associations:

我們的分析顯示以下關聯:

  1. Terrible => 0%

    很差=> 0%
  2. Poor => 5%

    差=> 5%
  3. Good => 10%

    好=> 10%
  4. Great => 15%

    很好=> 15%
  5. Excellent => 20%

    優秀=> 20%

In this case, a simple array or a simple list won’t be sufficient for holding the above associations. What’s the next simplest data structure that will allow us to implement these associations? After doing a little bit of research, we figure out that Hashtable is probably the most fitting data structure that can cover our needs with the least amount of ceremony.

在這種情況下,簡單的數組或簡單的列表不足以容納上述關聯。 允許我們實現這些關聯的下一個最簡單的數據結構是什么? 經過一些研究,我們發現Hashtable可能是最合適的數據結構,可以用最少的儀式滿足我們的需求。

We now navigate to the app directory and rename autogenerated Class1.cs file to TipCalculator.cs. We now want to add a Hashtable that will hold service ratings and the associated percentage values:

現在,我們導航到app目錄,并將自動生成的Class1.cs文件重命名為TipCalculator.cs 。 現在,我們要添加一個Hashtable ,該Hashtable表將保存服務評級和相關的百分比值:

System.Collections.Hashtable ratingPercentages = new System.Collections.Hashtable();

Now is a good time to recall that TDD is focused on coupling the expectations to the application’s behaviour, not to the application’s structure. Knowing that, we need to modify our test to make Tip Calculator exhibit some behaviour. The test codifies some expectations with regards to how the application must behave, and the running application provides the evidence of the expected behaviour.

現在是回想一下TDD專注于將期望與應用程序的行為而非應用程序的結構耦合的好時機。 知道這一點,我們需要修改測試以使“ Tip Calculator表現出某些行為。 該測試將有關應用程序必須如何行為的一些期望匯總起來,正在運行的應用程序提供了預期行為的證據。

But what is the evidence of the application’s behaviour? There is no other way for us to assess and evaluate application’s behaviour other than through examining the values that the running application produces.

但是,該應用程序行為的證據是什么? 除了檢查正在運行的應用程序產生的值之外,我們沒有其他方法可以評估和評估應用程序的行為。

In this case, we are expecting the running application to produce values true or false (Boolean values) after we ask the application if certain value (i.e. service rating) is valid.

在這種情況下,我們希望正在運行的應用程序在詢問某個值(例如,服務等級)是否有效之后會產生值truefalse (布爾值)。

To teach the application how to behave in the expected fashion, we need to endow it with an API. In this case, we design the API as follows:

要教應用程序如何以預期的方式工作,我們需要為它提供一個API。 在這種情況下,我們將API設計如下:

public bool CheckIfRatingIsValid(string rating)

In our test, we will modify the actual expected value to exercise the running application and collect the output value:

在我們的測試中,我們將修改實際期望值以執行正在運行的應用程序并收集輸出值:

As you can see from the screenshot above, we have instantiated TipCalculator but when attempting to ask the instance to check if the supplied rating (“Terrible”) is valid, the editor is complaining that it cannot find that method.

從上面的屏幕快照中可以看到,我們已經實例化了TipCalculator但是當嘗試要求實例檢查所提供的等級(“可怕”)是否有效時,編輯器抱怨說找不到該方法。

Well of course, the method hasn’t been implemented yet. Now’s the time to go ahead and do it:

當然,該方法尚未實現。 現在是時候繼續做下去了:

public bool CheckIfRatingIsValid(string rating) {	return false;
}

Now that the method is implemented, the test works; here is the complete listing:

現在已經實現了該方法,測試就可以進行; 這是完整的清單:

using Xunit;
using app;namespace tests {	public class TipCalculatorTests {		TipCalculator tipCalculator = new TipCalculator();		[Fact]		public void CheckIfRatingTerribleIsValid(){			var expectedResponseForValidRating = true;			var actualResponseForValidRating = tipCalculator.CheckIfRatingIsValid("Terrible");			      Assert.Equal(expectedResponseForValidRating, actualResponseForValidRating);		}	}
}

We see from the above example that we’re cheating again (we have hardcoded return false; in our newly minted method). What’s the point of beating around the bush and merely creating skeletons and scaffoldings instead of rolling up our sleeves and doing actual coding? Let’s discuss this important topic.

從上面的示例中我們看到我們再次作弊(在我們新創建的方法中,我們已經硬編碼return false;)。 在灌木叢中跳動,僅創建骨架和腳手架,而不是卷起袖子并進行實際編碼,有什么意義? 讓我們討論這個重要的話題。

討論我們的第一個設計決策 (Discussion about our first design decision)

We’re illustrating here how to do TDD step-by-step. The funny part is that this step-by-step illustration is actually the exact way how we do TDD: step-by-step. There is no other way to do TDD than by doing it step-by-step. One step at a time.

我們在這里說明如何逐步進行TDD。 有趣的部分是,此分步說明實際上是我們進行TDD的確切方法:分步。 除了逐步進行之外,沒有其他方法可以進行TDD。 一步一步來。

How’s that different from any other way of doing software development? Don’t we also do everything step-by-step even when not following TDD methodology? Well, not really. Let me explain:

與進行軟件開發的任何其他方式有何不同? 即使不遵循TDD方法,我們也不一步一步地做所有事情嗎? 好吧,不是真的。 讓我解釋:

TDD to me feels like riding a galloping horse. We’re moving swiftly toward our goal, but we’re frequently touching the ground (the galloping horse is every now and then hitting the ground in order to bounce off and run fast).

TDD對我來說就像騎著駿馬。 我們正在Swift向目標邁進,但我們經常碰到地面(奔騰的馬現在不時地撞到地面,以便反彈并快速奔跑)。

In comparison, when I’m doing software development without TDD, it feels to me like I’m flying a kite. I’m making swift moves with the kite, but I never touch the ground, not even once. By the time I land the kite, the landing place may not be where I intended the kite to go (it’s very hard to control the direction of a kite if it’s flying in a strong wind).

相比之下,當我在沒有TDD的情況下進行軟件開發時,對我來說就像在放風箏。 我正在用風箏快速移動,但我從未觸地,甚至沒有觸地。 到我放風箏時,降落的地方可能不是我想要放風箏的地方(如果風很大,很難控制風箏的方向)。

With TDD, any time we make a change to the code (both the test code and the shipping application code), we run the tests and so we touch the ground. We are galloping, but at the same time we need this frequent grounding. We need to see whether we’re going in the right direction and also whether we’ve broken anything during our galloping. Our tests are the Oracle who keeps telling us if everything works as expected or if something started misbehaving.

使用TDD時,只要我們更改代碼(測試代碼和運輸應用程序代碼),就可以運行測試,因此就可以使用。 我們在疾馳,但同時我們需要這種頻繁的接地。 我們需要查看我們是否朝著正確的方向前進,以及在疾馳過程中是否損壞了任何東西。 我們的測試是Oracle,它會不斷告訴我們一切是否按預期進行,或者某些事情開始出現異常。

Making changes to the code is a risky business. TDD provides a nice harness that is both guiding our design decisions and ensuring we don’t mess up something that we’ve already confirmed works to our expectations.

更改代碼是一項冒險的業務。 TDD提供了一個很好的工具,既可以指導我們的設計決策,又可以確保我們不會弄亂我們已經確認能達到預期效果的東西。

用實際的處理邏輯替換硬編碼的值 (Replace hardcoded value with actual processing logic)

Let’s now replace the hardcoded value with actual running code. We first teach our Tip Calculator that there is a service rating called “Terrible” and that tip percentage associated with this rating is 0:

現在讓我們用實際的運行代碼替換硬編碼的值。 我們首先告訴我們的Tip Calculator ,有一個服務評級為“可怕”,并且與該評級相關的小費百分比為0:

public bool CheckIfRatingIsValid(string rating) {	  ratingPercentages.Add("Terrible", 0);	return false;
}

Our Tip Calculator is now knowledgeable about the fact that there is a service rating labeled “Terrible” and the tip percentage associated with terrible service is 0%. Great, but we’re still returning hardcoded value false. Time to replace it with actual calculation:

現在,我們的Tip Calculator可以了解以下事實:服務等級標記為“糟糕”,并且與糟糕服務相關的小費百分比為0%。 很好,但是我們仍然返回硬編碼值false 。 是時候用實際計算替換它了:

public bool CheckIfRatingIsValid(string rating) {	ratingPercentages.Add("Terrible", 0);	return ratingPercentages.ContainsKey(rating);
}

Run the test again:

再次運行測試:

Great, but the code still looks contrived. We are loading the “Terrible” value into the instance of Hashtable ratingPercentages and then immediately checking to see if that value exists in the Hashtable. Now that we have moved from the failing test (Red) to the passing test (Green), it’s time to perform the third step in the TDD loop – Refactor.

很好,但是代碼看起來還是人為的。 我們正在將“可怕”值加載到Hashtable ratingPercentages實例中,然后立即檢查該值是否存在于Hashtable 。 現在我們已經從失敗測試(紅色)移至通過測試(綠色),是時候執行TDD循環中的第三步–重構。

Refactoring is basically the activity of modifying the code structure without affecting the code behaviour. Our task here is simple: extract the code responsible for populating of the Hashtable ratingPercentages into a separate block of code.

重構基本上是在不影響代碼行為的情況下修改代碼結構的活動。 我們的任務很簡單:將負責填充Hashtable ratingPercentages的代碼提取到單獨的代碼塊中。

The most natural place for this loading is in the block of code that is doing the initialization of the Tip Calculator – the constructor method. After refactoring, our shipping application source code looks like this:

進行加載的最自然的地方是進行Tip Calculator初始化的代碼塊- constructor方法。 重構后,我們的運輸應用程序源代碼如下所示:

using System.Collections;namespace app {	public class TipCalculator {		private Hashtable ratingPercentages = new Hashtable();		public TipCalculator() {			ratingPercentages.Add("Terrible", 0);		}		public bool CheckIfRatingIsValid(string rating) {			return ratingPercentages.ContainsKey(rating);		}	}
}

Run the test again, and it passes (we’re in green). We have modified the structure of the code without modifying its behaviour! Good job.

再次運行測試,測試通過(綠色)。 我們已經修改了代碼的結構,而沒有修改其行為! 做得好。

擲硬幣 (Flip the coin)

Any time we satisfy a positive expectation, it is a prudent practice to turn things on their head and describe the negative expectation.

每當我們滿足積極的期望時,明智的做法就是轉過頭去描述消極的期望。

At this point, since we’ve satisfied that a legitimate service rating value is found in the Tip Calculator, we want to ensure that non-legitimate values are not found in the Tip Calculator.

在這一點上,由于我們已經滿意在Tip Calculator找到了合法的服務評級值,因此我們要確保在Tip Calculator中未找到不合法的值。

What do we mean by non-legitimate values? Any value other than “Terrible”, “Poor”, “Good”, “Great” and “Excellent”. Time to write the new expectation (i.e. test):

非合法價值是什么意思? “差”,“差”,“好”,“好”和“優秀”以外的任何值。 是時候寫出新的期望了(即測試):

[Fact]
public void CheckIfRatingWhateverIsValid() {	var expectedResponseForValidRating = true;	var actualResponseForValidRating = 			           tipCalculator.CheckIfRatingIsValid("Whatever");Assert.Equal(expectedResponseForValidRating, actualResponseForValidRating);
}

Run the tests:

運行測試:

Fails. As expected. (We specified that our expectation when supplying the service rating as “Whatever” should be true. In reality, it is false, because our Tip Calculator does not contain value “Whatever”.)

失敗 如預期的那樣。 (我們指定在提供“任何”服務等級時的期望應該是true 。實際上,這是false ,因為我們的Tip Calculator不包含值“任何”。)

Fix the test (change the expectedResponseForValidRating from true to false) and run it again:

修復測試(將expectedResponseForValidRatingtrue更改為false )并再次運行它:

A moment of reflection: why did we fake the first test run and made it fail? Because we always want to make sure we observe our new test failing. That way, we’ll know that in the future any successful passing of the test is not merely a false positive.

片刻的反思:為什么我們要偽造第一次測試并使其失敗? 因為我們一直想確保我們觀察到新測試失敗。 這樣,我們就會知道,將來任何成功通過測試的都不僅僅是假陽性。

贊美穩態 (In praise of steady state)

Software engineering is a balancing act between steady state and periods of unstable state. What do we mean by steady state?

軟件工程是穩定狀態和不穩定狀態周期之間的平衡行為。 穩定狀態是什么意思?

If we have a system (a running application) that behaves the way we expect it to behave (i.e. it produces values we have specified as expected values), we declare that the system is in a steady state. It is running, and delivering some value.

如果我們有一個系統(正在運行的應用程序)以我們期望的方式運行(即產生我們指定為期望值的值),則我們聲明該系統處于穩定狀態。 它正在運行,并提供了一些價值。

That value delivery is still partial. In our case, the only value to the users this Tip Calculator delivers is its ability to recognize service rating “Terrible” as a legitimate rating. In addition, it is capable of informing us that service rating “Whatever” is not a legitimate rating.

價值傳遞仍然是部分的。 在我們的案例中,此Tip Calculator對用戶而言是唯一的價值 交付是其將服務等級“糟糕”識別為合法等級的能力。 此外,它還可以告知我們服務等級“無論如何”都不是合法的等級。

That’s not much, but still is better than nothing. And good news – our running application is currently in a steady state. Now we want to look into how to add more valuable behaviour to our Tip Calculator. And the only way to add more value is by making some changes.

數量不多,但總比沒有好。 好消息–我們正在運行的應用程序目前處于穩定狀態。 現在,我們要研究如何向“ Tip Calculator添加更多有價值的行為。 而增加價值的唯一方法是進行一些更改。

Any time we make a change to our application, we disturb its steady state. This disturbance is risky. It may mean our changes could break something that is already working. Because of that concern, we strive to make the duration of this unstable state as short as possible.

每當我們對應用程序進行更改時,我們都會破壞其穩態。 這種干擾是危險的。 這可能意味著我們的更改可能會破壞已經起作用的某些功能。 因此,我們努力使這種不穩定狀態的持續時間盡可能短。

Remember how we compared TDD to riding a galloping horse? When the horse is in flight (i.e. not touching the ground) it is advancing toward our goal, but it’s not in the steady state. Only when the horse touches the ground does its state stabilize.

還記得我們如何將TDD與騎馬馳horse相提并論嗎? 當馬在飛行中(即不接觸地面)時,它正在向我們的目標前進,但它不是處于穩定狀態。 只有當馬接觸地面時,其狀態才會穩定。

TDD encourages making small changes (in flight) and immediately grounding the system by verifying that it is back in the steady state. We value steady state despite the fact that we eagerly embrace changes. Without changes, we won’t be able to deliver value, but we must do it in a very deliberate, careful fashion.

TDD鼓勵進行微小的更改(在飛行中),并通過驗證系統是否處于穩定狀態來立即將其接地。 盡管我們熱切地接受變化,但我們仍重視穩定狀態。 沒有變化,我們將無法交付價值,但是我們必須以一種非常謹慎,謹慎的方式來實現它。

When doing TDD, we treat changes to steady state like walking on eggshells. No matter how sure we may be in knowing what and how we’re doing software engineering, it is prudent to still let failing tests guide our decisions.

在進行TDD時,我們像在蛋殼上行走一樣對待穩態的變化。 無論我們有多確定要知道什么以及如何進行軟件工程,還是要謹慎地讓失敗的測試來指導我們的決策。

檢查是否正確的小費百分比與服務等級相關聯 (Check if correct tip percentage is associated with service rating)

Let’s now introduce another change into our application – a test to verify if correct tip percentage is associated with service rating “Terrible”. Remember that we populated the instance of Hashtable ratingPercentages with the following values:

現在,讓我們在應用程序中進行另一個更改–測試以驗證正確的筆尖百分比是否與“可怕”服務等級相關聯。 請記住,我們使用以下值填充了Hashtable ratingPercentages實例:

ratingPercentages.Add("Terrible", 0);

We have written a test that verifies that our Hashtable ratingPercentages does contain legitimate service rating “Terrible”. Now we need a test that verifies that service rating “Terrible” means that the tip percentage for that rating is 0.

我們編寫了一個測試,以驗證我們的Hashtable ratingPercentages是否包含合法的服務等級“ Terrible”。 現在,我們需要進行一項測試,以驗證服務等級“糟糕”是否意味著該等級的小費百分比為0。

[Fact]
public void CheckIfRatingTerribleHasZeroPercentTip() {	var expectedZeroPercentForTerribleRating = 0;	var actualZeroPercentForTerribleRating = 10;	Assert.Equal(expectedZeroPercentForTerribleRating, actualZeroPercentForTerribleRating);
}

The new test CheckIfRatingTerribleHasZeroPercentTip should fail:

新測試CheckIfRatingTerribleHasZeroPercentTip應該失敗:

Again, we’re purposefully hard coding wrong actual values just so that we could observe our brand new test fail. Now we must replace hard coded value with the actual call to the Tip Calculator’s method that returns tip percentage for the service rating:

同樣,我們有目的地硬編碼錯誤的實際值,以便可以觀察到全新的測試失敗。 現在,我們必須用對Tip Calculator方法的實際調用替換硬編碼的值,該方法返回服務等級的提示百分比:

[Fact]
public void CheckIfRatingTerribleHasZeroPercentTip() {	var expectedZeroPercentForTerribleRating = 0;	var actualZeroPercentForTerribleRating = 			    tipCalculator.GetPercentageTipForRating("Terrible");Assert.Equal(expectedZeroPercentForTerribleRating, actualZeroPercentForTerribleRating);
}

As in the previous case, we have invented a new API for Tip Calculator. We call this new capability GetPercentageTipForRating("Terrible"). It takes the value of the service rating and returns the tip percentage for that rating.

與前面的情況一樣,我們為Tip Calculator發明了一個新的API。 我們將此新功能GetPercentageTipForRating("Terrible") 。 它采用服務等級的值,并返回該等級的小費百分比。

Flip over to the app/TipCalculator.cs and add the hard coded skeleton of the new method:

翻轉到app/TipCalculator.cs并添加新方法的硬編碼框架:

public int GetPercentageTipForRating(string rating) {	return 10;
}

Running the test fails again, because we have hard coded the return value. Let’s replace it with actual processing:

由于我們已經對返回值進行了硬編碼,因此運行測試再次失敗。 讓我們用實際處理代替它:

public int GetPercentageTipForRating(string rating) {	int tipPercentage = Int32.Parse(ratingPercentages[rating].ToString());return tipPercentage;
}

Run the test again:

再次運行測試:

All three tests pass – we’re in green, we’re back to steady state!

所有三個測試均通過–我們處于綠色狀態,我們回到了穩定狀態!

對于非合法的服務評級,我們期望什么小費百分比? (What tip percentage do we expect for non-legitimate service ratings?)

Many years of experience in the field taught me to be a bit pessimistic. Now that we have our application back in the steady state and delivering value (answering questions about legitimate service ratings and also giving us correct tip percentage for the “Terrible” rating), we need to see what happens when we run our application by giving it non-legitimate service rating value (for example, by giving it service rating “Whatever”).

在該領域的多年經驗教會我有些悲觀。 現在我們已經使應用程序恢復到穩定狀態并交付了價值(回答有關合法服務等級的問題,并為我們提供了“可怕”等級的正確提示百分比),我們需要通過提供應用程序來查看運行應用程序時會發生什么不合法的服務等級值(例如,將其服務等級定為“ Whatever”)。

Time for leaving the steady state yet again. We will write another test:

是時候再次離開穩定狀態了。 我們將編寫另一個測試:

[Fact]
public void CheckIfRatingWhateverHasNegativeOnePercentTip() {	var expectedZeroPercentForWhateverRating = -1;	var actualZeroPercentForWhateverRating = 	  tipCalculator.GetPercentageTipForRating("Whatever");Assert.Equal(expectedZeroPercentForWhateverRating, actualZeroPercentForWhateverRating);
}

We are describing our expectation when Tip Calculator is asked to return tip percentage for service rating “Whatever”. Because service rating “Whatever” is a non-legitimate rating, we are expecting Tip Calculator to return tip percentage of value -1.

當要求Tip Calculator返回服務等級為“任何??”的小費百分比時,我們正在描述我們的期望。 由于服務等級“無論如何”都是不合法的等級,因此我們希望Tip Calculator返回值-1的小費百分比。

This test now precipitates one improvement to our shipping code. We need to add some logic to first check whether the supplied service rating is legitimate or not. Only if it is legitimate do we ask Hashtable ratingPercentages to tell us what the associated value of the tip percentage is. If the supplied service rating is non-legitimate (for example, if it is “Whatever”) we bypass talking to Hashtable ratingPercentages and simply return -1.

現在,此測試可對我們的運輸代碼進行改進。 我們需要添加一些邏輯以首先檢查所提供的服務等級是否合法。 僅在合法的情況下,我們才要求Hashtable ratingPercentages告訴我們小費百分比的關聯值是多少。 如果提供的服務等級Hashtable ratingPercentages (例如,如果是“ Whatever”),我們將忽略與Hashtable ratingPercentages并僅返回-1。

public int GetPercentageTipForRating(string rating) {	int tipPercentage = -1;	if(CheckIfRatingIsValid(rating)) {		tipPercentage = Int32.Parse(ratingPercentages[rating].ToString());	}	return tipPercentage;
}

Run the tests, and all 4 tests pass:

運行測試,所有4個測試均通過:

We are back to the steady state. Another short excursion into the volatile area, and another swift victory and a safe return to steady, imperturbable state.

我們回到了穩定狀態。 再次短暫進??入不穩定地區,又一次Swift獲勝,安全返回穩定,穩定的狀態。

填充其他服務等級提示百分比 (Populate other service rating tip percentages)

Now is a good time to take a breather and make less risky changes, following the already established pattern. Leave the safety of the steady state and make short trips into the volatile territory by adding a new test to verify if service rating “Poor” is a valid, legitimate rating:

現在是按照已經建立的模式喘口氣并進行較小風險更改的好時機。 通過添加新的測試來驗證服務等級“差”是否是有效的合法等級,從而保持穩定狀態的安全,并短暫進入不穩定區域:

[Fact]
public void CheckIfRatingPoorIsValid() {	var expectedResponseForValidRating = true;	var actualResponseForValidRating = 	  tipCalculator.CheckIfRatingIsValid("Poor");Assert.Equal(expectedResponseForValidRating, actualResponseForValidRating);
}

Running this test will fail:

運行此測試將失敗:

Service rating “Poor” hasn’t been implemented yet. To make the test pass, implement service rating “Poor” by adding this line to the TipCalculator constructor:

服務等級“差”尚未實施。 要使測試通過,請將此行添加到TipCalculator構造函數中,以實現服務等級“差”:

ratingPercentages.Add("Poor", 5);

Run the tests, and we’re back to safety:

運行測試,我們回到安全狀態:

We’re enjoying steady state with 6 tests successfully passing.

我們正在通過6個測試成功通過的狀態保持穩定。

Now that we have added service rating “Poor” associated with the 5% tip, let’s write a test that will describe that expectation:

現在,我們添加了與5%的小費相關的服務評級“差”,讓我們編寫一個測試來描述這種期望:

[Fact]
public void CheckIfRatingPoorHasFivePercentTip() {	var expectedZeroPercentForPoorRating = 5;	var actualZeroPercentForPoorRating = 	  tipCalculator.GetPercentageTipForRating("Poor");Assert.Equal(expectedZeroPercentForPoorRating, actualZeroPercentForPoorRating);
}

The tests run successfully, and we’re back to being safe in the steady state.I will leave it to the reader to make the changes that will drive the implementation of the service ratings “Good”, “Great” and “Excellent”. At the end of the exercise you should have your system back in the steady state with 12 tests successfully passing:

測試成功運行,我們已回到穩定的安全狀態。我將它留給讀者進行更改,以推動實現“好”,“好”和“優秀”服務等級。 在練習結束時,您應該使系統恢復穩定并成功通過12個測試:

給定總數和服務等級,計算總數 (Calculate grand total given the total and the service rating)

We are now ready for the final step – given the total bill and the service rating, we expect Tip Calculator to calculate tip percentage and add it to the total, producing the grand total to be paid to the restaurant.

現在我們準備好進行最后一步了-給定總賬單和服務等級,我們希望Tip Calculator能夠計算小費百分比并將其加到總計中,從而產生要支付給餐廳的總計。

As we always do, first we describe the expectation:

像往常一樣,首先我們描述期望:

[Fact]
public void CalculateTotalWithTip() {	var expectedTotalWithTip = 135.7;	var actualTotalWithTip = 200.0;	Assert.Equal(expectedTotalWithTip, actualTotalWithTip);
}

As usual, we first hard code some expectations that we know are going to fail. This is so that we observe our new test failing:

像往常一樣,我們首先硬編碼一些我們知道會失敗的期望。 這樣我們可以觀察到我們的新測試失敗了:

Time to implement processing logic that will calculate correct total with tip. Given the total of $118.0, and the service rating “Great” (15% tip), we’re expecting the total to be $135.7:

是時候實施處理邏輯,用小費計算正確的總數。 鑒于總金額為118.0美元,服務評級為“很好”(小費15%),我們預計總金額為135.7美元:

[Fact]
public void CalculateTotalWithTip() {	var rating = "Great";	var total = 118;	var expectedTotalWithTip = 135.7;	var actualTotalWithTip = tipCalculator.CalculateTotalWithTip(total, rating);	Assert.Equal(expectedTotalWithTip, actualTotalWithTip);
}

We have designed a new API the Tip Calculator – a method called CalculateTotalWithTip(total, rating). It takes the total value and the service rating and returns the total with tip. The implementation of the method looks like this:

我們設計了一個新的API Tip Calculator -一種稱為CalculateTotalWithTip(total, rating) 。 它獲取總價值和服務等級,并帶小費返回總和。 該方法的實現如下所示:

public double CalculateTotalWithTip(double total, string rating) {	double totalWithTip = -1;	if(CheckIfRatingIsValid(rating)) {		int percentage = GetPercentageTipForRating(rating);		totalWithTip = total + ((total/100) * percentage);	}	return totalWithTip;
}

Run the tests, and we’re back to steady state:

運行測試,我們回到穩定狀態:

我們在這里完成嗎? (Are we done here?)

No, not yet. Even when all tests are in green and we’re back to the steady state, there are still a couple of things we need to do.

還沒有。 即使所有測試都呈綠色,并且我們又回到了穩定狀態,仍然需要做幾件事。

To begin with, we need to add a pessimistic expectation for our Tip Calculator calculation of total with the tip based on the service rating:

首先,我們需要根據服務等級為Tip Calculator的總費用添加悲觀期望:

[Fact]
public void CalculateTotalWithTipForNonlegitimateRating() {	var rating = "Meh";	var total = 118;	var expectedTotalWithTip = 135.7;	var actualTotalWithTip = tipCalculator.CalculateTotalWithTip(total, rating);	Assert.Equal(expectedTotalWithTip, actualTotalWithTip);
}

Running the tests produces one failing test:

運行測試會產生一個失敗的測試:

Our expectation for non-legitimate service rating (“Meh”) was incorrect. The actual total is -1, so we need to adjust our expectation by replacing 135.7 with -1. Run the tests again, and we’re back to the steady state!

我們對非合法服務等級(“ Meh”)的預期不正確。 實際總數為-1,因此我們需要通過將-135.7替換為-1來調整期望值。 再次運行測試,我們回到了穩定狀態!

We now have 14 tests, they all successfully pass, and our Tip Calculator works according to our expectations and satisfies the acceptance criteria.

現在,我們有14個測試,它們都成功通過了,并且我們的小費計算器可以按照我們的期望工作并滿足驗收標準。

We’re almost done. One more sanity check before we can confidently ship our shiny new Tip Calculator – we must run mutation testing.

我們快完成了。 在我們可以放心地發送閃亮的新Tip Calculator之前,還需要進行一次完整性檢查-我們必須運行突變測試

Our mutation testing framework will mutate the shipping code, one line at a time, and will run all tests for each individual mutation.

我們的突變測試框架將一次更改一行代碼的運輸代碼,并將針對每個單獨的突變運行所有測試。

If the tests complain about the mutated code, all is good, we have killed the mutant. If the tests don’t complain, we’re in trouble. We have a surviving mutant in our codebase, which means there are lines of code in our repo that are doing something for which we haven’t provided any expectations.

如果測試抱怨突變的代碼,那一切都很好,我們已經殺死了該突變體。 如果測試沒有問題,我們就麻煩了。 我們的代碼庫中有一個尚存的變體,這意味著我們的存儲庫中有一些代碼行正在做一些我們沒有想到的事情。

Let’s run mutation testing to see how solid our solution is. Good news – our solution has killed 100% of mutants!

讓我們運行突變測試,看看我們的解決方案有多牢固。 好消息–我們的解決方案殺死了100%的突變體!

Mutation testing has given our shipping application a clean bill of health. Our Tip Calculator seems to be in good shape.

突變測試為我們的運輸應用程序提供了清晰的健康清單。 我們的Tip Calculator似乎狀態良好。

紅綠重構反射 (Red-Green-Refactor-Reflect)

Let’s review our Tip Calculator building exercise. We started the process by describing our expectations using the classical user story format. User story (as the name implies) is focused on describing scenarios that fulfill end user’s goals.

讓我們回顧一下Tip Calculator構建練習。 我們通過使用經典用戶故事格式描述我們的期望來開始該過程。 用戶故事(顧名思義)專注于描述實現最終用戶目標的方案。

In this case, the simple goal is to calculate the tip amount from the supplied service rating and the restaurant bill total. The calculated tip amount is then automatically added to the total.

在這種情況下,簡單的目標是從提供的服務等級和餐廳賬單總額中計算小費金額。 然后將計算出的小費金額自動添加到總數中。

From there we proceeded to build our shipping application by following the TDD methodology. As we’ve demonstrated, the methodology consists of writing a failing test, observing it fail (the Red phase of TDD), then immediately making code changes that ensure the test passes (the Green phase of TDD). Once the test passes, we move into the Refactor phase (we restructure the code without affecting its behaviour). That way, we make sure our code is not expensive to change.

從那里開始,我們遵循TDD方法來構建運輸應用程序。 如我們所展示的,該方法包括編寫一個失敗的測試,觀察它是否失敗(TDD的紅色階段),然后立即進行代碼更改以確保測試通過(TDD的綠色階段)。 測試通過后,我們進入重構階段(我們在不影響其行為的情況下重構了代碼)。 這樣,我們確保更改代碼的代價并不昂貴。

A proper TDD practice also mandates frequent retrospective – we call it reflection. We stop and think about the things we’ve accomplished thus far, to see if we could learn from our recent experiences. This reflection fortifies the process, as it relies on frequent and tight feedback provided by the failing, then succeeding tests.

適當的TDD做法也要求經常進行回顧-我們稱其為反思。 我們停下來想一想到目前為止我們已經完成的事情,看看我們是否可以從最近的經驗中學到東西。 這種反映加強了該過程,因為它依賴于失敗的測試和隨后的測試所提供的頻繁而嚴格的反饋。

I have already compared Test Driven Development to the experience of riding a galloping horse. While riding a horse, we’re alternating between flying through the air (i.e. speed achieved when the horse leaps from the ground) and steering the horse. It is impossible to steer the horse while we’re off the ground, up in the air. At that point, we gain speed, but we cannot make any changes of the direction. It is only once the horse touches the ground that we can make a change in direction.

我已經將“測試驅動開發”與“奔馬”的體驗進行了比較。 騎馬時,我們在空中飛行(即,馬從地面跳下時達到的速度)和操縱馬之間進行切換。 當我們離開地面,懸而未決時,不可能操縱馬匹。 在這一點上,我們可以提高速度,但是不能改變方向。 只有當馬觸地時,我們才能改變方向。

In TDD, we strive to touch the ground as frequently as possible. The longer the leaps we make without touching the ground, the less chance we have for correcting the course.

在TDD中,我們努力盡可能多地接觸地面。 我們在不觸地面的情況下進行的跳躍越長,糾正路線的機會就越少。

I also compared software development practices that don’t follow TDD principles to the experience of flying a kite. When flying a kite, we never touch the ground. It is an exhilarating feeling of letting the wind pick the kite up and bounce it up in the air. We can achieve considerable speed that way. But we struggle in such situations to maintain desired course. And after we eventually land the kite, it usually does not land in the spot we originally wanted it to land.

我還將不遵循TDD原理的軟件開發實踐與放風箏的體驗進行了比較。 放風箏時,我們永遠不會觸地。 讓風把風箏拾起并在空中彈起,這是一種令人振奮的感覺。 這樣我們可以達到可觀的速度。 但是我們在這樣的情況下努力維持預期的路線。 在我們最終放下風箏后,它通常不會降落在我們最初希望放下的地方。

Why is the emphasis of this article on “don’t write tests first”? Many software engineers who are not familiar with agile practices as implemented in TDD usually either claim that writing automated tests isn’t necessary, or claim that automated tests should be written after the code is complete.

為什么本文強調“不要先編寫測試”? 許多不熟悉TDD中實現的敏捷實踐的軟件工程師通常要么聲稱不需要編寫自動測試,要么聲稱應該在代碼完成后編寫自動測試。

Once they start learning about agile and TDD, they may reconsider their practices and decide that writing tests before writing implementation code may make more sense. Still, because of the ingrained waterfall mentality, some of those engineers make the mistake of writing all tests first, and only then move into writing the code.

一旦他們開始學習敏捷和TDD,他們可能會重新考慮他們的實踐,并決定在編寫實現代碼之前編寫測試可能更有意義。 但是,由于根深蒂固的瀑布思維,其中一些工程師犯了先編寫所有測試,然后才編寫代碼的錯誤。

That approach is completely wrong. It is equivalent to the traditional waterfall approach where we go through the development process by respecting gated phases.

這種方法是完全錯誤的。 它等效于傳統的瀑布方法,在這種方法中,我們會遵循選通階段來經歷開發過程。

First we write the requirements (in this case, requirements would be expectations written in the form of automated tests). Only once all the requirements (i.e. automated tests) have been written, signed off and frozen, do we move into the next gated phase – write the code for the shipping application.

首先,我們編寫需求(在這種情況下,需求就是以自動化測試的形式編寫的期望)。 只有在所有要求(即自動測試)均已編寫,簽字并凍結后,我們才能進入下一個封閉階段–編寫運輸應用程序的代碼。

TDD is the exact opposite of the “write tests first” approach. In TDD, we always write only one test. That test describes a desired behaviour. The desired behaviour does not exist yet (that’s why it is desired), and the test fails.

TDD與“先寫測試”方法完全相反。 在TDD中,我們總是只編寫一個測試。 該測試描述了期望的行為。 所需的行為尚不存在(這就是所需的原因),并且測試失敗。

We then immediately move into making changes to the code in the attempt to create the desired behaviour. Once desired behaviour is created, it gets validated by the test, and if the expectations of the test are satisfied, we move into refactoring the code (to satisfy nonfunctional requirements, such as cost of change).

然后,我們立即著手對代碼進行更改,以嘗試創建所需的行為。 一旦創建了期望的行為,它就會通過測試進行驗證,如果滿足了測試的期望,我們將著手重構代碼(以滿足非功能性需求,例如變更成本)。

We practice a rigorous discipline to never succumb to the temptation to write more than one test at a time. That way, we ensure that we keep touching the ground as frequently as possible.

我們實行嚴格的紀律,以免屈從于一次編寫多個測試的誘惑。 這樣,我們可以確保我們盡可能頻繁地接觸地面。

We prefer to remain ‘in flight’ for the shortest possible time. We are ‘in flight’ during that period when the desired behaviour described in the test has not materialized yet. The smaller the expected and desired behaviour is, the shorter will be our ‘in flight’ trajectory. That way, we keep touching the ground often, which gives us a chance to adjust the steering.

我們希望在最短的時間內保持“飛行”狀態。 當測試中描述的所需行為尚未實現時,我們正在“飛行中”。 預期和期望的行為越小,我們的“飛行中”軌跡就越短。 這樣,我們就經常與地面接觸,這給了我們調整轉向的機會。

結論 (Conclusion)

Building a simple Tip Calculator is a toy sized problem, and using that exercise to illustrate TDD methodology is not necessarily providing a convincing argument in favour of TDD. Still, within the constraints of a technical article, going over this hands-on exercise may provide valuable insights into the benefits of adopting TDD.

建立一個簡單的Tip Calculator是一個玩具大小的問題,使用該練習來說明TDD方法論并不一定提供支持TDD的令人信服的論點。 盡管如此,在技術文章的限制范圍內,繼續進行此動手練習可能會為采用TDD的好處提供有價值的見解。

We would still argue that the real benefits of TDD only become apparent when dealing with much larger, more complex software engineering efforts. The ability to remain grounded while making potentially risky changes to a large, complex system is often a life saver.

我們仍然認為,只有在處理更大,更復雜的軟件工程工作時,TDD的真正好處才會顯現出來。 在對大型復雜系統進行潛在風險更改時保持接地的能力通常可以挽救生命。

In addition to that, building software using TDD methodology results in much less rework. TDD drives high degree of modularization, which results in high cohesiveness of the modules and low coupling between the modules.

除此之外,使用TDD方法構建軟件可以減少返工。 TDD驅動了高度的模塊化,這導致了模塊的高內聚性和模塊之間的低耦合。

All these characteristics produce a shipping application whose codebase is easy and inexpensive to change. And lowering the cost of change has proven to be the best way on the path to embracing changes and abandoning the concept known as ‘scope creep’.

所有這些特征產生了一個運輸應用程序,其代碼庫易于更改且成本低廉。 事實證明,降低變更成本是擁抱變更并摒棄“范圍蔓延”概念的最佳途徑。

Bottom line, TDD enables software engineering teams to deliver high degree of flexibility to the business.

總而言之,TDD使軟件工程團隊能夠為業務提供高度的靈活性。

翻譯自: https://www.freecodecamp.org/news/dont-write-all-your-software-tests-first-just-write-one/

軟件測試測試用例編寫

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/390247.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/390247.shtml
英文地址,請注明出處:http://en.pswp.cn/news/390247.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

練習五

1.團隊模式和團隊的開發模式有什么關系&#xff1f; 答&#xff1a; 首先我來解釋一下這兩個名詞&#xff1a; 我查資料了解了一下&#xff0c;團隊模式&#xff0c;更偏向于多人合作的那種&#xff0c;而且我理解的“團隊”會是一種多人合作的情況下&#xff0c;長期磨合后的一…

Squid 訪問控制配置

Squid 訪問控制配置 主配置文件內加入限制參數 vim /etc/squid/squid.conf # 訪問控制 acl http proto HTTP # 限制訪問 good_domain添加兩個域名“.”表示半解析(相當于*) acl good_domain dstdomain .kevin.net .baidu.com # 允許good_domain內的域名訪問 http_access allow …

劍指 Offer 62. 圓圈中最后剩下的數字

0,1,,n-1這n個數字排成一個圓圈&#xff0c;從數字0開始&#xff0c;每次從這個圓圈里刪除第m個數字&#xff08;刪除后從下一個數字開始計數&#xff09;。求出這個圓圈里剩下的最后一個數字。 例如&#xff0c;0、1、2、3、4這5個數字組成一個圓圈&#xff0c;從數字0開始每…

rust 編程入門_面向初學者的Rust –最受歡迎的編程語言入門

rust 編程入門Rust has been voted Stack Overflow’s most loved programming language for five years in a row. This article will tell you why Rust is awesome.Rust已連續五年被評為Stack Overflow最受歡迎的編程語言。 本文將告訴您為什么Rust很棒。 Rust is a system…

【轉載】springboot:如何優雅的使用mybatis

這兩天啟動了一個新項目因為項目組成員一直都使用的是mybatis&#xff0c;雖然個人比較喜歡jpa這種極簡的模式&#xff0c;但是為了項目保持統一性技術選型還是定了 mybatis。到網上找了一下關于spring boot和mybatis組合的相關資料&#xff0c;各種各樣的形式都有&#xff0c;…

創建react應用程序_通過構建電影搜索應用程序在1小時內了解React

創建react應用程序If youve been meaning to learn React but are unsure of where to start, Scrimbas brand new Build a Movie Search App course is perfect for you! 如果您一直想學習React&#xff0c;但是不確定從哪里開始&#xff0c;那么Scrimba全新的Build a Movie S…

GeoServer自動發布地圖服務

1 NetCDF氣象文件自動發布案例 GeoServer是一個地理服務器&#xff0c;提供了管理頁面進行服務發布&#xff0c;樣式&#xff0c;切片&#xff0c;圖層預覽等一系列操作&#xff0c;但是手動進行頁面配置有時并不滿足業務需求&#xff0c;所以GeoServer同時提供了豐富的rest接口…

selenium+ python自動化--斷言assertpy

前言&#xff1a; 在對登錄驗證時&#xff0c;不知道為何原因用unittest的斷言不成功&#xff0c;就在網上發現這個assertpy&#xff0c;因此做個筆記 準備&#xff1a; pip install assertypy 例子&#xff1a; 1 from assertpy import assert_that2 3 4 def check_login():5 …

11. 盛最多水的容器

11. 盛最多水的容器 給你 n 個非負整數 a1&#xff0c;a2&#xff0c;…&#xff0c;an&#xff0c;每個數代表坐標中的一個點 (i, ai) 。在坐標內畫 n 條垂直線&#xff0c;垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0) 。找出其中的兩條線&#xff0c;使得它們與 x 軸共同構…

深入理解ES6 pdf

下載地址&#xff1a;網盤下載目錄 第1章 塊級作用域綁定 1var聲明及變量提升&#xff08;Hoisting&#xff09;機制 1塊級聲明 3-- let聲明 3-- 禁止重聲明 4-- const聲明 4-- 臨時死區&#xff08;Temporal Dead Zone&#xff09; 6循環中的塊作用域綁定 7-- 循環中的函…

MyBatis之輸入與輸出(resultType、resultMap)映射

2019獨角獸企業重金招聘Python工程師標準>>> 在MyBatis中&#xff0c;我們通過parameterType完成輸入映射(指將值映射到sql語句的占位符中&#xff0c;值的類型與dao層響應方法的參數類型一致)&#xff0c;通過resultType完成輸出映射(從數據庫中輸出&#xff0c;通…

2021-08-25556. 下一個更大元素 III

556. 下一個更大元素 III 給你一個正整數 n &#xff0c;請你找出符合條件的最小整數&#xff0c;其由重新排列 n 中存在的每位數字組成&#xff0c;并且其值大于 n 。如果不存在這樣的正整數&#xff0c;則返回 -1 。 注意 &#xff0c;返回的整數應當是一個 32 位整數 &…

gradle tool升級到3.0注意事項

Gradle版本升級 其實當AS升級到3.0之后&#xff0c;Gradle Plugin和Gradle不升級也是可以繼續使用的&#xff0c;但很多新的特性如&#xff1a;Java8支持、新的依賴匹配機制、AAPT2等新功能都無法正常使用。 Gradle Plugin升級到3.0.0及以上&#xff0c;修改project/build.grad…

如何使用React,TypeScript和React測試庫創建出色的用戶體驗

Im always willing to learn, no matter how much I know. As a software engineer, my thirst for knowledge has increased a lot. I know that I have a lot of things to learn daily.無論我知道多少&#xff0c;我總是愿意學習。 作為軟件工程師&#xff0c;我對知識的渴望…

PowerDesigner常用設置

2019獨角獸企業重金招聘Python工程師標準>>> 使用powerdesigner進行數據庫設計確實方便&#xff0c;以下是一些常用的設置 附加&#xff1a;工具欄不見了 調色板(Palette)快捷工具欄不見了 PowerDesigner 快捷工具欄 palette 不見了&#xff0c;怎么重新打開&#x…

bzoj5090[lydsy11月賽]組題

裸的01分數規劃,二分答案,沒了. #include<cstdio> #include<algorithm> using namespace std; const int maxn100005; int a[maxn]; double b[maxn]; double c[maxn]; typedef long long ll; ll gcd(ll a,ll b){return (b0)?a:gcd(b,a%b); } int main(){int n,k;s…

797. 所有可能的路徑

797. 所有可能的路徑 給你一個有 n 個節點的 有向無環圖&#xff08;DAG&#xff09;&#xff0c;請你找出所有從節點 0 到節點 n-1 的路徑并輸出&#xff08;不要求按特定順序&#xff09; 二維數組的第 i 個數組中的單元都表示有向圖中 i 號節點所能到達的下一些節點&#…

深入框架本源系列 —— Virtual Dom

該系列會逐步更新&#xff0c;完整的講解目前主流框架中底層相通的技術&#xff0c;接下來的代碼內容都會更新在 這里 為什么需要 Virtual Dom 眾所周知&#xff0c;操作 DOM 是很耗費性能的一件事情&#xff0c;既然如此&#xff0c;我們可以考慮通過 JS 對象來模擬 DOM 對象&…

網絡工程師常備工具_網絡安全工程師應該知道的10種工具

網絡工程師常備工具If youre a penetration tester, there are numerous tools you can use to help you accomplish your goals. 如果您是滲透測試人員&#xff0c;則可以使用許多工具來幫助您實現目標。 From scanning to post-exploitation, here are ten tools you must k…

configure: error: You need a C++ compiler for C++ support.

安裝pcre包的時候提示缺少c編譯器 報錯信息如下&#xff1a; configure: error: You need a C compiler for C support. 解決辦法&#xff0c;使用yum安裝&#xff1a;yum -y install gcc-c 轉載于:https://www.cnblogs.com/mkl34367803/p/8428264.html