
- •Practical Unit Testing with JUnit and Mockito
- •Table of Contents
- •About the Author
- •Acknowledgments
- •Preface
- •Preface - JUnit
- •Part I. Developers' Tests
- •Chapter 1. On Tests and Tools
- •1.1. An Object-Oriented System
- •1.2. Types of Developers' Tests
- •1.2.1. Unit Tests
- •1.2.2. Integration Tests
- •1.2.3. End-to-End Tests
- •1.2.4. Examples
- •1.2.5. Conclusions
- •1.3. Verification and Design
- •1.5. Tools Introduction
- •Chapter 2. Unit Tests
- •2.1. What is a Unit Test?
- •2.2. Interactions in Unit Tests
- •2.2.1. State vs. Interaction Testing
- •2.2.2. Why Worry about Indirect Interactions?
- •Part II. Writing Unit Tests
- •3.2. Class To Test
- •3.3. Your First JUnit Test
- •3.3.1. Test Results
- •3.4. JUnit Assertions
- •3.5. Failing Test
- •3.6. Parameterized Tests
- •3.6.1. The Problem
- •3.6.2. The Solution
- •3.6.3. Conclusions
- •3.7. Checking Expected Exceptions
- •3.8. Test Fixture Setting
- •3.8.1. Test Fixture Examples
- •3.8.2. Test Fixture in Every Test Method
- •3.8.3. JUnit Execution Model
- •3.8.4. Annotations for Test Fixture Creation
- •3.9. Phases of a Unit Test
- •3.10. Conclusions
- •3.11. Exercises
- •3.11.1. JUnit Run
- •3.11.2. String Reverse
- •3.11.3. HashMap
- •3.11.4. Fahrenheits to Celcius with Parameterized Tests
- •3.11.5. Master Your IDE
- •Templates
- •Quick Navigation
- •Chapter 4. Test Driven Development
- •4.1. When to Write Tests?
- •4.1.1. Test Last (AKA Code First) Development
- •4.1.2. Test First Development
- •4.1.3. Always after a Bug is Found
- •4.2. TDD Rhythm
- •4.2.1. RED - Write a Test that Fails
- •How To Choose the Next Test To Write
- •Readable Assertion Message
- •4.2.2. GREEN - Write the Simplest Thing that Works
- •4.2.3. REFACTOR - Improve the Code
- •Refactoring the Tests
- •Adding Javadocs
- •4.2.4. Here We Go Again
- •4.3. Benefits
- •4.4. TDD is Not Only about Unit Tests
- •4.5. Test First Example
- •4.5.1. The Problem
- •4.5.2. RED - Write a Failing Test
- •4.5.3. GREEN - Fix the Code
- •4.5.4. REFACTOR - Even If Only a Little Bit
- •4.5.5. First Cycle Finished
- •‘The Simplest Thing that Works’ Revisited
- •4.5.6. More Test Cases
- •But is It Comparable?
- •Comparison Tests
- •4.6. Conclusions and Comments
- •4.7. How to Start Coding TDD
- •4.8. When not To Use Test-First?
- •4.9. Should I Follow It Blindly?
- •4.9.1. Write Good Assertion Messages from the Beginning
- •4.9.2. If the Test Passes "By Default"
- •4.10. Exercises
- •4.10.1. Password Validator
- •4.10.2. Regex
- •4.10.3. Booking System
- •Chapter 5. Mocks, Stubs, Test Spies
- •5.1. Introducing Mockito
- •5.1.1. Creating Test Doubles
- •5.1.2. Expectations
- •5.1.3. Verification
- •5.1.4. Conclusions
- •5.2. Types of Test Double
- •5.2.1. Code To Be Tested with Test Doubles
- •5.2.2. The Dummy Object
- •5.2.3. Test Stub
- •5.2.4. Test Spy
- •5.2.5. Mock
- •5.3. Putting it All Together
- •5.4. Example: TDD with Test Doubles
- •5.4.2. The Second Test: Send a Message to Multiple Subscribers
- •Refactoring
- •5.4.3. The Third Test: Send Messages to Subscribers Only
- •5.4.4. The Fourth Test: Subscribe More Than Once
- •Mockito: How Many Times?
- •5.4.5. The Fifth Test: Remove a Subscriber
- •5.4.6. TDD and Test Doubles - Conclusions
- •More Test Code than Production Code
- •The Interface is What Really Matters
- •Interactions Can Be Tested
- •Some Test Doubles are More Useful than Others
- •5.5. Always Use Test Doubles… or Maybe Not?
- •5.5.1. No Test Doubles
- •5.5.2. Using Test Doubles
- •No Winner So Far
- •5.5.3. A More Complicated Example
- •5.5.4. Use Test Doubles or Not? - Conclusion
- •5.6. Conclusions (with a Warning)
- •5.7. Exercises
- •5.7.1. User Service Tested
- •5.7.2. Race Results Enhanced
- •5.7.3. Booking System Revisited
- •5.7.4. Read, Read, Read!
- •Part III. Hints and Discussions
- •Chapter 6. Things You Should Know
- •6.1. What Values To Check?
- •6.1.1. Expected Values
- •6.1.2. Boundary Values
- •6.1.3. Strange Values
- •6.1.4. Should You Always Care?
- •6.1.5. Not Only Input Parameters
- •6.2. How to Fail a Test?
- •6.3. How to Ignore a Test?
- •6.4. More about Expected Exceptions
- •6.4.1. The Expected Exception Message
- •6.4.2. Catch-Exception Library
- •6.4.3. Testing Exceptions And Interactions
- •6.4.4. Conclusions
- •6.5. Stubbing Void Methods
- •6.6. Matchers
- •6.6.1. JUnit Support for Matcher Libraries
- •6.6.2. Comparing Matcher with "Standard" Assertions
- •6.6.3. Custom Matchers
- •6.6.4. Advantages of Matchers
- •6.7. Mockito Matchers
- •6.7.1. Hamcrest Matchers Integration
- •6.7.2. Matchers Warning
- •6.8. Rules
- •6.8.1. Using Rules
- •6.8.2. Writing Custom Rules
- •6.9. Unit Testing Asynchronous Code
- •6.9.1. Waiting for the Asynchronous Task to Finish
- •6.9.2. Making Asynchronous Synchronous
- •6.9.3. Conclusions
- •6.10. Testing Thread Safe
- •6.10.1. ID Generator: Requirements
- •6.10.2. ID Generator: First Implementation
- •6.10.3. ID Generator: Second Implementation
- •6.10.4. Conclusions
- •6.11. Time is not on Your Side
- •6.11.1. Test Every Date (Within Reason)
- •6.11.2. Conclusions
- •6.12. Testing Collections
- •6.12.1. The TDD Approach - Step by Step
- •6.12.2. Using External Assertions
- •Unitils
- •Testing Collections Using Matchers
- •6.12.3. Custom Solution
- •6.12.4. Conclusions
- •6.13. Reading Test Data From Files
- •6.13.1. CSV Files
- •6.13.2. Excel Files
- •6.14. Conclusions
- •6.15. Exercises
- •6.15.1. Design Test Cases: State Testing
- •6.15.2. Design Test Cases: Interactions Testing
- •6.15.3. Test Collections
- •6.15.4. Time Testing
- •6.15.5. Redesign of the TimeProvider class
- •6.15.6. Write a Custom Matcher
- •6.15.7. Preserve System Properties During Tests
- •6.15.8. Enhance the RetryTestRule
- •6.15.9. Make an ID Generator Bulletproof
- •Chapter 7. Points of Controversy
- •7.1. Access Modifiers
- •7.2. Random Values in Tests
- •7.2.1. Random Object Properties
- •7.2.2. Generating Multiple Test Cases
- •7.2.3. Conclusions
- •7.3. Is Set-up the Right Thing for You?
- •7.4. How Many Assertions per Test Method?
- •7.4.1. Code Example
- •7.4.2. Pros and Cons
- •7.4.3. Conclusions
- •7.5. Private Methods Testing
- •7.5.1. Verification vs. Design - Revisited
- •7.5.2. Options We Have
- •7.5.3. Private Methods Testing - Techniques
- •Reflection
- •Access Modifiers
- •7.5.4. Conclusions
- •7.6. New Operator
- •7.6.1. PowerMock to the Rescue
- •7.6.2. Redesign and Inject
- •7.6.3. Refactor and Subclass
- •7.6.4. Partial Mocking
- •7.6.5. Conclusions
- •7.7. Capturing Arguments to Collaborators
- •7.8. Conclusions
- •7.9. Exercises
- •7.9.1. Testing Legacy Code
- •Part IV. Listen and Organize
- •Chapter 8. Getting Feedback
- •8.1. IDE Feedback
- •8.1.1. Eclipse Test Reports
- •8.1.2. IntelliJ IDEA Test Reports
- •8.1.3. Conclusion
- •8.2. JUnit Default Reports
- •8.3. Writing Custom Listeners
- •8.4. Readable Assertion Messages
- •8.4.1. Add a Custom Assertion Message
- •8.4.2. Implement the toString() Method
- •8.4.3. Use the Right Assertion Method
- •8.5. Logging in Tests
- •8.6. Debugging Tests
- •8.7. Notifying The Team
- •8.8. Conclusions
- •8.9. Exercises
- •8.9.1. Study Test Output
- •8.9.2. Enhance the Custom Rule
- •8.9.3. Custom Test Listener
- •8.9.4. Debugging Session
- •Chapter 9. Organization Of Tests
- •9.1. Package for Test Classes
- •9.2. Name Your Tests Consistently
- •9.2.1. Test Class Names
- •Splitting Up Long Test Classes
- •Test Class Per Feature
- •9.2.2. Test Method Names
- •9.2.3. Naming of Test-Double Variables
- •9.3. Comments in Tests
- •9.4. BDD: ‘Given’, ‘When’, ‘Then’
- •9.4.1. Testing BDD-Style
- •9.4.2. Mockito BDD-Style
- •9.5. Reducing Boilerplate Code
- •9.5.1. One-Liner Stubs
- •9.5.2. Mockito Annotations
- •9.6. Creating Complex Objects
- •9.6.1. Mummy Knows Best
- •9.6.2. Test Data Builder
- •9.6.3. Conclusions
- •9.7. Conclusions
- •9.8. Exercises
- •9.8.1. Test Fixture Setting
- •9.8.2. Test Data Builder
- •Part V. Make Them Better
- •Chapter 10. Maintainable Tests
- •10.1. Test Behaviour, not Methods
- •10.2. Complexity Leads to Bugs
- •10.3. Follow the Rules or Suffer
- •10.3.1. Real Life is Object-Oriented
- •10.3.2. The Non-Object-Oriented Approach
- •Do We Need Mocks?
- •10.3.3. The Object-Oriented Approach
- •10.3.4. How To Deal with Procedural Code?
- •10.3.5. Conclusions
- •10.4. Rewriting Tests when the Code Changes
- •10.4.1. Avoid Overspecified Tests
- •10.4.2. Are You Really Coding Test-First?
- •10.4.3. Conclusions
- •10.5. Things Too Simple To Break
- •10.6. Conclusions
- •10.7. Exercises
- •10.7.1. A Car is a Sports Car if …
- •10.7.2. Stack Test
- •Chapter 11. Test Quality
- •11.1. An Overview
- •11.2. Static Analysis Tools
- •11.3. Code Coverage
- •11.3.1. Line and Branch Coverage
- •11.3.2. Code Coverage Reports
- •11.3.3. The Devil is in the Details
- •11.3.4. How Much Code Coverage is Good Enough?
- •11.3.5. Conclusion
- •11.4. Mutation Testing
- •11.4.1. How does it Work?
- •11.4.2. Working with PIT
- •11.4.3. Conclusions
- •11.5. Code Reviews
- •11.5.1. A Three-Minute Test Code Review
- •Size Heuristics
- •But do They Run?
- •Check Code Coverage
- •Conclusions
- •11.5.2. Things to Look For
- •Easy to Understand
- •Documented
- •Are All the Important Scenarios Verified?
- •Run Them
- •Date Testing
- •11.5.3. Conclusions
- •11.6. Refactor Your Tests
- •11.6.1. Use Meaningful Names - Everywhere
- •11.6.2. Make It Understandable at a Glance
- •11.6.3. Make Irrelevant Data Clearly Visible
- •11.6.4. Do not Test Many Things at Once
- •11.6.5. Change Order of Methods
- •11.7. Conclusions
- •11.8. Exercises
- •11.8.1. Clean this Mess
- •Appendix A. Automated Tests
- •A.1. Wasting Your Time by not Writing Tests
- •A.1.1. And what about Human Testers?
- •A.1.2. One More Benefit: A Documentation that is Always Up-To-Date
- •A.2. When and Where Should Tests Run?
- •Appendix B. Running Unit Tests
- •B.1. Running Tests with Eclipse
- •B.1.1. Debugging Tests with Eclipse
- •B.2. Running Tests with IntelliJ IDEA
- •B.2.1. Debugging Tests with IntelliJ IDEA
- •B.3. Running Tests with Gradle
- •B.3.1. Using JUnit Listeners with Gradle
- •B.3.2. Adding JARs to Gradle’s Tests Classpath
- •B.4. Running Tests with Maven
- •B.4.1. Using JUnit Listeners and Reporters with Maven
- •B.4.2. Adding JARs to Maven’s Tests Classpath
- •Appendix C. Test Spy vs. Mock
- •C.1. Different Flow - and Who Asserts?
- •C.2. Stop with the First Error
- •C.3. Stubbing
- •C.4. Forgiveness
- •C.5. Different Threads or Containers
- •C.6. Conclusions
- •Appendix D. Where Should I Go Now?
- •Bibliography
- •Glossary
- •Index
- •Thank You!

Chapter 4. Test Driven Development
Listing 4.15. compareTo() method recognizes lesser team
public int compareTo(FootballTeam otherTeam) { if (gamesWon > otherTeam.getGamesWon()) {
return 1;
}
else if (gamesWon < otherTeam.getGamesWon()) { return -1;
}
return 0;
}
All tests pass, so we can move on to an equality test:
Listing 4.16. Testing for equality
@Test
public void teamsWithSameNumberOfMatchesWonShouldBeEqual() { FootballTeam teamA = new FootballTeam(2);
FootballTeam teamB = new FootballTeam(2);
assertTrue("both teams have won the same number of games: "
+teamA.getGamesWon() + " vs. " + teamB.getGamesWon()
+" and should be ranked equal", teamA.compareTo(teamB) == 0);
}
Well, this test passes instantly, because our implementation has already returned 0 in cases of equality. So, what should we do now? We have definitely skipped one step in the TDD rhythm. We have never seen this equality test failing, so we do not know why it passes. Is it because the FootballTeam class really implements the expected behaviour, or is it because our test has been badly written and would always pass?
For the time being, let us assume that the test is flawless, and that the implementation of FootballTeam also is. We will come back to this discussion later on, when examining some corner cases of TDD (see Section 4.9).
Now that we have a safety net of tests we can really refactor the tested method. After having thought through the matter carefully, we have ended up with much simpler implementation:
Listing 4.17. compareTo() method simplified
public int compareTo(FootballTeam otherTeam) { return gamesWon - otherTeam.getGamesWon();
}
The rerunning of the tests now tells us that this implementation satisfies all the requirements (written in the form of tests) so far.
4.6. Conclusions and Comments
The actual implementation of the test is rather a detail of TDD. Much more important is the mindset of practicing baby steps, the mindset of gaining insights and evolving through rapid feedback, the mindset of leveraging trial & error as a methodology, where errors are not failures but valuable insights that guide the evolution of the project.
— Jonas Bandi
56

Chapter 4. Test Driven Development
So we have just implemented a class using the test-first approach. We moved in very small steps. We paid attention to the details. We polished the code. We even dealt with assertion messages. And all the time we were making sure that no functionality was broken – by rerunning the tests over and over again. And all the time we were moving forward… step by step… always forward.
I have a lot of questions to ask you now. How did you like it? Did you enjoy the red-green-refactor rhythm? Did it help you with writing the code, or did you feel it got in the way of your style of thinking and coding? Are you confident that the created code really works? Would you have done it better or faster without testing, or – maybe – would you have preferred to have written the tests after the implementation had finished? Did you feel you were wasting time by writing tests even for trivial cases? Did you feel confident refactoring the code?
Such a simple example might not be enough to demonstrate its usefulness. The problem was so trivial that you surely would have had an implementation ready in your mind from the outset, and would not have benefitted much from having written the tests first. However, it will be different when you start using test-first "in the wild". You will be writing tests for features that you have no idea how to implement. Then you will see how having tests helps you come up with a solution.
Personally, I feel I owe so much to the test-first technique that I would be delighted if you also were to like it. However, it is up to you to decide whether test-first really suits you. Throughout the rest of this book I will be relying solely on the test-first technique. If you like it, great! If not, well, please do bear with it anyway!
It would be sensible to have a look at the benefits of TDD as listed in Section 4.3. Have you taken note of them? Is your code 100% covered by your tests? Any "possibly important" features implemented? Is it simple? Is it clean and readable? Have you noticed the difference when continuing your work after an interruption? If so, maybe TDD is worth the effort!
One additional comment regarding our tests written using test-first. As you have probably noticed, each test created its own object of the FootballTeam class. Usually a good idea is to create the SUT in a set-up (which is an example of extract method refactoring) so that each test automatically gets a new one (in a clean, well-known state). However, in this case it was not possible, because each test method required a football team that had been created differently.
4.7. How to Start Coding TDD
We are all the same, under pressure we fall back on what we know; hit a few difficulties in TDD and developers stop writing tests.
— Ian Cooper
So now you have read about the wonders of the TDD approach! And you like it! It seems a little bit awkward, but at the same time sensible. You have heard about some great programmers writing tests before code. You have heard about some projects written test-first and renowned for their superior quality. You have spent some time reading about TDD techniques, you know the rhythm, you are getting more and more excited about the whole idea (up to the point of tattooing a "red-green-refactor" mantra on your back). Eventually… you have made up your mind about it: you want to try it… you want to be a step ahead of your team mates. You are eager to try it right now, with the very next job that you are given!
And you do try it. And it feels like… like trying to break a wall with your own head! Databases, Spring context, static classes, ORM tools, JNDI calls, web services, global variables, deep inheritance hierarchy
57

Chapter 4. Test Driven Development
and console output jump out at you all at once. Nothing looks like it did in the test-first tutorials; none of the good advice you read seems of any help. It is just plain impossible to proceed!
But of course you do not give up without a fight. Struggling, with gritted teeth, you somehow overcome one issue after another, one at a time. It takes time. You need to read some blogs, talk with your colleagues about the necessary design changes, but somehow it works. But at the end of the day, you look at the amount of written code, and you know that this is not good. You would have achieved 10 times more by using the code-first approach that you are accustomed with. You are not even proud of your tests - you know what dirty hacks you had to use to be able to write them.
Let us stop here, before we sink into despair. Dear TDD-Wannabe-Programmer, you were simply trying to do too much at once! You were trying to change your habits (which is hard enough in itself) and, at the same time, to solve multiple problems existing in your codebase. Even if you did have some tests (as I hope you did), your code was probably never written with testability in mind. Trying to add new features in test-first manner is like challenging the old codebase: it will fight back, as you have just experienced. It will attempt to thwart your efforts. Those same solutions that once you were so proud of are now stopping you from moving forwards.
What I would suggest is exactly what TDD itself promotes. Move in small steps. Start with the simplest things. Implement easy tasks using the test-first approach. Cherish every TDD success. Learn from every TDD failure. If you find something that you cannot tackle using the test-first approach, then do not worry too much about it. After a few weeks (hours, days, months?) you will be able to deal with such tasks. Build up your experience in small steps. Day by day… This way it will work – you will see!
It will get easier and easier every day. Firstly, because you are getting better and better at coding testfirst. You "feel the rhythm", you concentrate on the task in hand, you stop thinking about possible and useful issues that are merely hypothetical, etc. Secondly, because you stop doing things you were doing, and start thinking in terms of testing. Thirdly, your design is much more testable now, and the next parts of your code are easier to test. Fourthly, some colleagues have joined you and stopped producing tons of untestable code. …and now you can see the light at the end of the tunnel. Good. :)
4.8. When not To Use Test-First?
TDD works like a charm, and it is very probable that once you get a good grip on it, you will be reluctant to code in any other way. However, there are some circumstances where this approach does not seem to be the best option. This section discusses this.
As explained in the previous section, it is really not advisable to jump at the deep end after just reading a "how-to-swim” tutorial. :) I do have faith in your programming skills (and so on), but it might just happen to be the case that you are not yet ready to implement any real-life programming tasks using the test-first approach. Start with something simple, gain experience and then move on.
This means that the first anti-test-first situation is attacking all possible problems at the same time, while lacking experience, skills and confidence. Do not do that.
And even then, when you have some experience, you will still find yourself stuck with TDD. Like Kent Beck9, who tried to implement a new Eclipse plugin without prior knowledge of the technology:
For six or eight hours spread over the next few weeks I struggled to get the first test written and running. […] If I’d just written some stuff and verified it by hand, I would
9Do I really have to introduce Kent Beck? If so… see http://en.wikipedia.org/wiki/Kent_Beck
58