The Art of Unit Testing and TDD in Java
21 lessons 3 h 38 min 3 sections
Start course Watch on YouTube playlist (external link, opens in new tab)
Recorded live during a three day class.
About this course
Roy Osherove teaches unit testing (external link, opens in new tab) and test-driven development (external link, opens in new tab) in Java over three days, starting with units of work (external link, opens in new tab), JUnit (external link, opens in new tab) test structure and naming, and string calculator (external link, opens in new tab) code written test-first. The second day covers mocks and stubs (external link, opens in new tab), first handwritten and then with isolation frameworks (external link, opens in new tab) such as Mockito, EasyMock and jMock. The third day covers extract and override (external link, opens in new tab), faking time (external link, opens in new tab), the unconstrained frameworks (external link, opens in new tab) PowerMock and JMockit, and a closing talk on build automation (external link, opens in new tab) and continuous delivery (external link, opens in new tab).
Curriculum
Section 1
Day 1
The first day covers what a unit of work is and its three kinds of end results, the structure and naming of JUnit tests, keeping unit and integration tests apart, and testing exceptions. It then introduces test-driven development with string calculator demos in IntelliJ, discusses simplicity and incremental steps, answers common TDD questions and presents an experiment comparing a team that used TDD with one that did not.
- Introduction to Unit Testing Ideas Duration 28:25
Roy Osherove separates good unit testing from test-driven development, which he traces to extreme programming and Kent Beck's book Test-Driven Development by Example and calls a personal practice. He defines a unit as a unit of work that starts at a public API and ends in a return value, a noticeable state change or a third-party call, the only place for mock objects. He argues for one test per end result and questions claims that unit tests make code easier to debug, maintain and understand, as when 50 tests stop compiling after a constructor change.
- Introduction to JUnit Duration 13:48
Roy Osherove contrasts manual test rigs with xUnit frameworks and introduces JUnit 4 with a test of a string calculator's add method laid out as arrange, act, assert. He explains his three-part test names (unit of work, scenario, expected behavior) and why the before, after, before class and after class attributes usually point to integration tests. He defines unit tests as running in memory under full control and keeps them in a separate package from integration tests, giving a safe green zone of tests that should always pass.
- Unit Testing Demo Duration 7:30
In IntelliJ, Roy Osherove shows string calculator tests that use helper methods called make calc and assert adding to avoid repetition and keep the tests maintainable. He then compares ways to test that a line throws an exception in JUnit: a try/catch block, the expected parameter of the test annotation, which he advises against because any line can throw, and an expected exception rule. With the rule, only the lines after the expect call are supposed to throw the exception.
- Introducing TDD Duration 12:53
Roy Osherove gives the three rules of test-driven development: start with a failing test, make it work in the simplest way possible, then make it better. He adds a subtract method to the string calculator test-first, choosing simple values such as one and two and deciding the order of the parameters while the code does not exist yet. He makes the test fail by returning minus one before implementing the method, and shows the IntelliJ run configuration that runs all JUnit tests in the package.
- Simplicity is Complicated Duration 11:37
Roy Osherove judges how simple a solution is by how close it is to the beginning of the method and how little it is indented. He test-drives a parse method on the string calculator, starting with an empty string that returns zero, and treats each hard-coded answer as an assumption that the next failing test, such as a single number, proves wrong. He connects these small steps to incremental development in micro iterations, with new working code that could be released every day.
- Common TDD Questions and Answers Duration 5:49
Roy Osherove answers why tests are written before the code: to test the test, to think about the problem before the solution, and because there is never time to write them after the code. He says TDD makes developers try to prove that their code doesn't work, that the tests remove the fear of refactoring, and that doing the simplest thing avoids writing code nobody uses. He also explains why he has only one failing test at a time and writes the planned tests as comments.
- Some Numbers Duration 5:37
Roy Osherove describes an experiment at a big company where two teams adapted the same features of a billing system, one with TDD and one without. The TDD team took 14 days instead of seven to implement the features but released in 24 days instead of 26, partly because its unit tests showed where integration bugs were not, and it had 90% fewer bugs in production. He says many companies don't try this because they fear investing without seeing the return for months.
Section 2
Day 2
The second day is about testing calls to third parties with fakes, defining mocks and stubs and injecting a handwritten fake logger and fake web service into the string calculator. It then moves to isolation frameworks, comparing the strict and non-strict styles of jMock, EasyMock and Mockito, and shows how to capture call arguments with Mockito's argument captor.
- Introduction to Mocks and Stubs Duration 10:30
Roy Osherove explains how to test a call to a third party with a string calculator that must send each sum to a slow logger, by adding a logger interface and a handwritten fake logger class. He defines a fake as something that looks like something else, a mock as a fake you assert against and a stub as any other fake, and allows at most one mock per test. The fake is passed in through constructor injection, and a test name such as add always calls logger shows that a mock object is involved.
- Demo - Mock Objects Duration 9:17
Roy Osherove writes the add always calls logger test first, as if everything already existed, with a fake logger that saves the text in a public written field and is passed to the string calculator's constructor. Because the other tests create the calculator through the make calc helper, the new constructor argument changes one place, where a fake logger is passed in as a stub. He sees only the new test fail, makes add call the logger, and has to add throws Throwable to the tests, which he dislikes.
- Terminology Duration 0:56
Roy Osherove contrasts his simple terminology with the book xUnit Test Patterns, which gives many different names to things that look like other things. He only uses fakes: a fake that is asserted on is a mock, any other fake is a stub, and terms such as test spy can be learned later.
- Mocks And Stubs Duration 8:43
Roy Osherove adds a requirement that a web service is called when the slow logger throws an exception, for example when the disk is full on a machine. The fake logger becomes a stub with a will throw method that sets the exception to simulate, and a fake web service becomes the mock object that the test asserts on. He writes the add when logger throws calls web service test with an out of memory error, and says the test name tells you what to do when the fakes get confusing.
- Isolation (Mocking) Frameworks Duration 19:53
Roy Osherove explains that isolation frameworks, which he prefers not to call mocking frameworks, automate handwritten fakes: they create fakes, save call arguments for verification and simulate exceptions or return values. He lists EasyMock, jMock, PowerMock, JMockit and Mockito, and uses jMock's expectations and EasyMock's record and replay to show how strict fakes, which fail on unexpected calls, break tests for the wrong reason. He recommends EasyMock's nice mocks and shows Mockito, which is non-strict by default, with verify, doThrow and parameter matchers such as any string.
- Capturing Mock Arguments Duration 4:36
Roy Osherove shows Mockito's argument captor for checking that a trace message with specific attributes was sent to the logger, by capturing the argument in a verify call and asserting on its value. He calls this clumsy and compares it with a handwritten fake class, which has to change whenever a method is added to the logger interface. A participant suggests implementing equals on the trace message, which he says can be the most readable way when the object is simple.
Section 3
Day 3
The third day deals with dependencies that cannot simply be replaced, such as static methods, the current time and objects created with new, using extract and override and a time provider class. It then shows the unconstrained frameworks PowerMock and JMockit and discusses their trade-offs, and ends with a talk on build automation and continuous delivery and a short closing summary.
- Extract And Override Refactoring Duration 13:35
Roy Osherove separates constrained isolation frameworks, which cannot override static or private methods, from unconstrained ones such as JMockit and PowerMock, which can fake them. Without them, he applies extract and override to a string calculator that calls a static logger: the call moves into a protected call logger method, which a testable subclass in the test overrides to save the text in a public field. He also explains seams, keeping logic out of the extracted method, and starting legacy code with integration tests before refactoring.
- Faking Time Duration 5:46
Roy Osherove explains why tests should not depend on the current date and time, and why extract and override or a time provider interface in every constructor does not suit a cross-cutting concern like time. Instead he uses a static time provider class in the production code that tests can force to a given time and reset afterwards, for example in an after method. For the machine name in the next exercise he suggests extract and override.
- Faking Futures Duration 2:41
Roy Osherove discusses an exercise with a login manager that creates a new slow logger, where the test has to prove that the logger's write method is called. He compares extracting a protected method that returns the slow logger, which needs a fake subclass of it, with extracting the whole call into a protected method, which he usually prefers as simpler.
- Unconstrained Isolation Frameworks Duration 9:50
Roy Osherove shows the static logger test written with PowerMock, Mockito and JUnit, using the run with annotation with the PowerMock runner, an annotation that loads the static logger class differently, and verify static before each verified call. He then shows the same tests in JMockit, which has to be loaded before JUnit and uses non-strict expectations and verifications blocks. He does not usually recommend JMockit because calls are repeated twice and tests get wordy, and suggests starting the exercises with PowerMock.
- Throw Errors with Powermock Duration 1:21
Roy Osherove shows how to make the write method of a static logger throw an exception with PowerMock: after mock static, doThrow with an out of memory exception is set on the static logger class, and the next call to the static logger is the one that throws. He notes that this API is not obvious at first and that a matcher such as any string tells the reader more.
- Discussion About Pros and Cons of Unconstrained Frameworks Duration 2:14
After the exercises Roy Osherove asks how unconstrained frameworks felt, concludes that they were much more complicated than refactoring, and explains that an API for faking private and static methods, constructors and other legacy code features has to be as complex as legacy code. He says tools like PowerMock and JMockit let you test code without changing it but give up the readability and maintainability that refactoring brings, so they should be used like a nuclear weapon.
- Continuous Delivery Duration 42:33
Drawing on his book in progress Beautiful Build, Roy Osherove calls releases the best form of compilation and separates build controllers such as TeamCity or Jenkins, build agents and build scripts by what each should know. He recommends starting with a shipping skeleton, a hello world application that goes through the whole release chain, and covers cumulative builds sharing artifacts, snapshot dependencies, build script injection and parameterized FinalBuilder scripts. He also discusses the bus factor, build waves that let developers resume coding once CI passes, and why breaking the build should not be discouraged.
- Summary Duration 0:35
Roy Osherove ends the course by pointing to artofunittesting.com for more videos and free material, to his Twitter account RoyOsherove and to osherove.com for courses. He mentions that he is writing books about beautiful builds and about team leaders in software.