The depth of interpretation - TDD Details (Test Driven Development)

This paper is structured:

  • What is TDD
  • Why TDD
  • How TDD
  • FAQ
  • Learning Path
  • Further reading

What is TDD

TDD broad and narrow sense, narrow sense is often say TDD, which is UTDD (Unit Test Driven Development). TDD is generalized ATDD (Acceptance Test Driven Development), comprising a BDD (Behavior Driven Development) and Consumer-Driven Contracts Development like.
As referred to herein TDD TDD refers to the narrow sense, that is, "unit test-driven development."

TDD is a core agile development practices and technologies, but also a design methodology. TDD principle is developed before the function code, unit test cases before writing the code, test code to determine what products need to write code. TDD is XP (Extreme Programming) of the core practices. Its main promoter is Kent Beck.

TDD has three meanings:

  • Test-Driven Development, Test Driven Development.
  • Task-Driven Development, mission-driven development, to analyze the problem and task decomposition.
  • Test-Driven Design, designed to improve the protection under test. TDD does not directly improve design capability, it just gives you more opportunities to protect and improve the design.

Why TDD

Traditional encoding VS TDD encoding

Traditional encoding

  • Needs analysis, want to know details of who cares, first began to write
  • It found that demand details are not clear, talk to business people confirmed
  • Confirm several times and finally finished all logic
  • Running test, rely on, it really does not work, commissioning
  • Long time and finally commissioning work
  • Turn test, QA measured bug, debug, patch
  • Finally, the code can work
  • A look at the code like rotten lump of feces, afraid to move, move had to manual testing, QA testing had to make, have to work overtime ...

TDD encoding

  • First task decomposition, separation of concerns (followed by a presentation)
  • Column Example, with examples of demand, demand clarification details
  • Write tests only focus on the needs, input and output of the program, do not care about the middle of the process
  • Write realize, nothing else needs to meet current needs with this small easiest way to
  • Reconstruction, with the way the code to eliminate the bad taste
  • Finished, manual test, basically no problem, there is a problem make use cases, repair
  • Turn test, a small problem, make use cases, repair
  • Code is clean and complete use case, confidently submit

TDD benefits

Reduce the burden on developers
through a clear process, let us first focus on one point, the burden of thinking smaller.

Protective net
benefit of TDD is completely covered unit testing, product code provides a safety net, so that we can easily meet the demand for change or improve the design code .
So if your project needs a stable, one-time finish, no subsequent changes to it, can enjoy the benefits of TDD is relatively less.

Advance clarification needs
to write test can help us to think about the needs and requirements in advance to clarify details, rather than code written half only to find no clear demand.

Quick feedback
when there are a lot of people say TDD, my code amount is increased, the development efficiency is reduced. However, if there is no unit tests, you have to manual testing, you spend a lot of time to prepare data, launch applications, such as jump interface, feedback is very slow. Precisely speaking, the feedback is rapid benefits of unit testing.

How TDD

TDD

The basic flow of TDD is: red, green and reconstruction.
More detailed process is:

  • Write a test case
  • Run the test
  • Write just let realize passing tests
  • Run the test
  • Identifying bad taste, by way modify the code
  • Run the test

You may ask me to write a test case, it will obviously fail, but also to run it?
Yes. You might think that only test of success and failure in both cases, however, there are numerous multiple failures, run the test to ensure that the current failure is your expectation of failure.
Everything is in order to allow the program in line with expectations, so that when errors occur, you can quickly locate the error (it must have been caused by just modified code, because the code is still a minute ago in line with my expectations).
In this way, saving a lot of time debugging code.

TDD's three rules

  1. Unless it is to make a failing unit test pass, otherwise not allowed to write any production code
  2. In a unit test, only allows you to write content just to be able to lead to failure (compilation error can be considered failed)
  3. Only allows you to write just to make a failing unit test by product code

If a violation of what will happen?
The first violation, first write the product code, and that code is to achieve what needs? How to ensure that it really implements it?
The second violation, wrote a number of failed tests, if the test can not be a long time, will increase the pressure of developers, in addition, the test might be reconstructed, then the modification will increase the cost of testing.
In violation of Article III, the product code to achieve functionality beyond the current test, then this part of the code would be no protection test, do not know is correct, the need for manual testing. Maybe this is non-existent demand, out of thin air it increases the complexity of the code. If the demand is there, and that subsequent tests will be written directly undermine the rhythm of TDD.

I think the essence of it is:
separation of concerns, once only a hat
in the course of our programming, there are several concerns: demand, implementation, design.
TDD has given us a clear three steps, each focus on one aspect.
Red: Write a failing test, it is a description of a small demand, only we need to care about input and output, this time simply do not care how.
Green: focus on the fastest way to achieve this is currently little demand, do not care about other needs, and do not control how appalling quality of the code.
Reconstruction: neither think pressure needs, nor implement, just need to find the code in bad taste, and eliminate it with a way to allow the code to become clean code.

Attention control
people's attention either active control, passive also be attracted. Attention switched back and forth, it would consume more energy, you will not think less complete.
Using TDD development, we should take the initiative to control attention, write tests, I found a class is not defined, IDE hint to the compiler error, this time to go if you create this class, your attention is not on demand, and has switched to the realization, we should focus on to finish this test, consider whether it expresses the need to determine the correct and then begin to eliminate compilation errors.

TDD why many people do not do it?

Split task is not reasonable
to split the task before the TDD, the demolition of a large demand into smaller demand.
A plurality of functions can also be removed.

Not write test
what is effective unit testing, there are a lot of people write tests, even measured in what in the end is not clear, it may not even assert, to verify the console output, visual comparison.
Good unit tests should comply with several principles:

  • Simple, only needs a test
  • In line with Given-When-Then format
  • high speed
  • It contains the assertion
  • Can repeat

Not write just realized
when a lot of people can not focus on writing implement current needs, accidentally put the needs of others also realized, it undermines the sense of rhythm.
When implemented not small strides.

Reconstruction will not
understand what is Clean Code, see Smell, no timely reconstruction, such as when you want to start the reconstruction has been difficult.
I do not know the right "way" to eliminate Smell.

Infrastructure
for a particular technology stack, unit testing did not build a good infrastructure, making it impossible to focus on test cases when writing tests.

Examples


Write a program to calculate the frequency of a text file words.txt each word appears.
To keep things simple, assume that:

  • words.txt contain only lowercase letters and spaces
  • Each word contains only lowercase letters
  • Between words separated by one or more spaces

For example, assume the following words.txt comprising:

the day is sunny the the
the sunny is is

Your program should output the following, according to the frequency reverse order:

the 4
is 3
sunny 2
day 1


Please do not read on, think about how you would do.
(Think three minutes ...)

This novice needs to get it, it will put all the code written to a main () method, the pseudo-code is as follows:

main() {
    // 读取文件
    ...
    // 分隔单词
    ...
    // 分组
    ...
    // 倒序排序
    ...
    // 拼接字符串
    ...
    // 打印
    ...
}

Very clear idea, but often finished in one breath, and finally up and running, the output is not as expected, then began to break point debugging.

Such code without any encapsulation. That's why a lot of people hear that some companies limit a method of not more than 10 lines, flew out of it, this is not possible, the line 10 can do ah, our business logic is very complicated ...
so what kind of code exists the problem?

  • Untestable
  • Can not be reused
  • Difficult to locate the problem

Okay, then we come TDD Well, you say read files, console output test code how to write?
Of course, we can isolate IO by Mock and Stub, but really necessary?

Someone asked such a question Kent Beck:

What would you really measure it? Even getter and setter will test it?

Kent Beck said: The company asked me to come in order to achieve business value, instead of writing test code.
So I just write test code in place no confidence.

That for our program, read documents and printed to the console is the calling system API, it can be very confident. Most do not have confidence that the middle write to write their own business logic.
So we can do some package program, "the code clean way," Lane said, the place can be annotated extraction method, the method name instead NOTE:

main() {
    String words = read_file('words.txt')
    String[] wordArray = split(words)
    Map<String, Integer> frequency = group(wordArray)
    sort(frequency)
    String output = format(frequency)
    print(output)
}

This is not to be separate split, group, sort, formatthe way to write unit tests do?
Certainly, their input and output are very clear thing.

And so on, you might say, not test-driven design? How do you begin to design? good question!

TDD want it designed to advance it?


Kent Beck is not designed in advance, he would choose one of the simplest use case, write directly open, with the most simple code to pass the test. Gradually increase the test, so that the code becomes complex, with reconstruction to drive the design.
In this demand, the simplest scenario what is it?
That is, the file is empty, the output is empty.

Of course, for complex problems, you may want to add a new side while writing use cases, but for this simple problem, basically wanted to know in advance what use cases to drive what the product code.
You can probably think of the following use cases:

  • "" => ""
  • "He" => "he 1", a word, driven out of the code string formatted
  • "He is" => "he 1 \ r \ nis 1", two different words, driven out of the code word segmentation
  • "He he is" => "he 2 \ r \ nis 1", the same word, the code packet driven out
  • "He is is" => "is 2 \ r \ nhe 1", driving the sorting code packets
  • "He is" => "he 1 \ r \ nis 1", multiple spaces, perfect division of code words

Martin Fowler's view is that, before we write the code to do Big Design Up Front , before starting to write the code to design all the details well.
And after we have reconstructed this tool, do the design pressure a lot smaller because of test code protection, we can always reconstruct reality. But this does not mean that we do not need to advance the design, design in advance so that we can discuss with others, you can first iteration several times before you start writing code, change the code on paper iteration is better than faster.
I personally agree with Martin Fowler practice, first in the mind (of course, my mind is not enough, so the paper painting) do the design, and then began to write after several iterations, so, I will use the simplest implementation through testing, but there is a direction reconstruction, more efficient.

Back to this program, I found that the current package is not on a level of abstraction, design is more desirable:

Task decomposition
main() {
    String words = read_file('words.txt')
    String output = word_frequency(words)
    print(output)
}

word_frequency(words) {
    String[] wordArray = split(words)
    Map<String, Integer> frequency = group(wordArray)
    sort(frequency)
    return format(frequency)
}

At this time, there are two options, it was like a top-down, bottom-up it was like, I personally prefer the former.


Now, just follow the red - green - you can do remodeling cycle.
Most TDD done, that there is no decomposition process of the task ahead and column Example of.
TDD want to see the process, you can refer me to do live .
Or, if necessary, I can record a video on this topic.

FAQ

Why must first write the test, trial and supplementary after not?

OK, but after the finish to achieve, immediately write tests with tests to verify implementation. If you first manual testing, debugging the code is good, fix the unit test, you will feel very sad, but also increased the workload.
Whether or post-test first row and enjoy a fast feedback, but if the first test, you can enjoy another benefit, intent-driven programming using less rework. Because your test code is the product code of the client (the caller), you can write your ideal look like (the method name, parameters, return values, etc.), go to realize the product code in the test code, compared to after-write for writing to test, the former less rework.

Just write a test, not writing implement. You know that the test will run error, why go running?

In fact, not only the results of tests run by and not two, because there are many possible, is not passed. So that we know certain failure conditions continue to run the test, the purpose is to see if it is reported that mistake desired.

Small strides really good, but you really need such a small step?

Mai pace too easily pulled eggs.
Practice time need to develop the habit of small steps, when the work can freely switch the size of pace.
When the self-confidence when you step on it big point, when you are less confident when you can immediately switch to small step mode. If only big, it is difficult to re small step.

To test whether the code will become a maintenance burden?

TDD also follow the maintenance procedures to modify the test code to look after the needs change, so that the test failed, and then modify it by product code.
So you're not in the maintenance of test cases, but in the use of test cases.

Why should quickly achieve?

In fact, is to find a dichotomy to isolate the problem by hardcode achieved through the test, the basic test to determine there is no problem, then go to achieve product code, if the test is not passed, the problem is the product code.
So small strides primarily to isolate the problem, that is, you can say goodbye to the Debug.

Why test code to be very simple?

If a test fails, when the repair is to change the test code instead of the product code, test code that is badly written.
When the test code is simple enough, if a test fails, it must be concluded that there is sufficient confidence in the products in question codes.

When not suitable for TDD?

If you are exploratory research and technology (Spike) do not require long-term maintenance, and testing infrastructure to build costly manual testing that was it.
There are also "very poor testability legacy systems" and "use test unfriendly technology stack" system, TDD may do more harm than good.

Learning Path

  1. "Effective unit testing."
  2. "Code neat way"
  3. "Reconstruction"
  4. Transformation Priority Premise
  5. 《Test-Driven Development by Example》
  6. 《Growing Object-Oriented Software, Guided by Tests》

Further reading

This article was originally published on GitChat, the article has been published for free, welcome to buy a Q & Record.

Original Address: https://www.jianshu.com/p/62f16cd4fef3

Guess you like

Origin www.cnblogs.com/jpfss/p/10966027.html