Growing Object Oriented Software, Guided By Tests - Book Summary
Andrea Angella
Technical Lead @ Redgate Software | ?Former Six-Times MVP on C# | ?? Grow Software Engineering Leaders | ?? Trainer, Mentor and Coach @ productivecsharp.com | ?? Host of The Productive C# Podcast | ?? Books Reader
This post was originally written on the Productive C# blog in 2014.
The book present the interesting approach of starting a new project with a?Walking Skeleton ?that is a tiny implementation of the system that performs a small end-to-end function
The point of the walking skeleton is to help us understand the requirements well enough to propose and validate a broad-brush system structure.
In most Agile projects, there’s a first stage where the team is doing initial analysis, setting up its physical and technical environments, and otherwise getting started. This is usually called?Iteration Zero.
One important task for iteration zero is to use the walking skeleton to?test-drive the initial architecture.
Working through our first end-to-end test will force some of the structural decisions we need to make, such as packaging and deployment.
We always have a single command that reliably compiles, builds, deploys, and tests the application, and that we run it repeatedly. We only start coding once we have an automated build and test working.
Other interesting points are about the effects of this level of initial exploration:
A sense of urgency will help the team to strip the functionality down to the absolute minimum sufficient to test their assumptions.
We often found it worth writing a small amount of ugly code and seeing how it falls out. It helps us to test our ideas before we’ve gone too far, and sometimes the results can be surprising. The important point is to make sure we don’t leave it ugly.
The book recommend the following things:
Test Driven Development ?combines testing, specification and design into one holistic activity. Difficulty in testing might imply that we need to change our test code, but often it’s a hint that our design ideas are wrong and that we ought to change the production code.
We’ve found that the qualities that make an object easy to test also make our code responsive to change. The trick is to let our tests drive our design
TDD is about testing code, verifying its externally visible qualities such as functionality and performance. TDD is also about feedback on the code’s internal qualities: the coupling and cohesion of its classes, dependencies that are explicit or hidden, and effective information hiding—the qualities that keep the code maintainable.
One of the importance goal the authors have is structuring code to make the boundaries of objects clearly visible.
An object should only deal with values and instances that are either local—created and managed within its scope—or passed in explicitly.
The book describes through examples a way of doing TDD that heavily rely on using mocks. I personally rarely used this style and I tend to prefer the more traditional TDD approach as presented by?Kent Beck ?in his book?TDD By Example .
The reason why they love this style is that emphasize how objects communicate, rather than what they are, so that they end up with types and roles defined more in terms of the domain than of the implementation.
They also say that:
领英推荐
In terms of dependencies, they insist on dependencies being passed in to the constructor, but notifications and adjustments can be set to defaults and reconfigured later.
They strongly believe in the principle of “Tell, Don’t Ask ”
For me, the best piece of advice in the book is the?Test Data Builder ?pattern. I have started to use this pattern heavily in my team in Redgate Software ?and our test are now incredibly more readable and flexible. I really recommend you to have a look at the pattern and see if it can be a good fit for you.
We find that test data builders help to reduce duplication, keep tests expressive and resilient to change.
First, they wrap up most of the syntax noise when creating new objects. Second, they make the default case simple, and special cases not much more complicated. Third, they protect the test against changes in the structure of its objects.
We can write test code that’s easier to read and spot errors, because each builder method identifies the purpose of its parameter.
Combined with factory methods and test scaffolding, test data builders help us write more literate, declarative tests that describe the intention of a feature, not just a sequence of steps to drive it.
We can even use higher-level tests to communicate directly with non-technical stakeholders, such as business analysts
An another thing I totally agree on is the?importance of good test diagnostic.
The last thing we should have to do is crack open the debugger and step through the tested code to find the point of disagreement.
We’ve learned the hard way to make tests fail informatively. If a failing test clearly explains what has failed and why, we can quickly diagnose and correct the code. Then, we can get on with the next task.
The easiest way to improve diagnostics is to keep each test small and focused and give tests readable names.
Sometimes tests are a little bit fragile especially when you create assertions on strings.
One interesting effect of trying to write precise assertions against text strings is that the effort often suggests that we’re missing an intermediate structure object. Most of the code would be written in terms of this intermediate object, a structured value that carries all the relevant fields.
The book terminates discussing the challenges of testing multi threading code.
The only solution is to make the system deterministic.
This also means that we are no longer exercising the entire system so some slow system testing is required to increase fidelity.