Showing posts with label Unit Testing. Show all posts
Showing posts with label Unit Testing. Show all posts

Thursday, April 30, 2015

Software Development Lessons Learned from Consumer Experience

Because we software developers are also inevitably consumers of others' software applications, we are undoubtedly influenced in the creation of our own software by the software we use. For example, our opinions of what makes an effective interface for users are influenced by our experiences "on the other side" using someone else's software interface. This is particularly true for web and mobile development as web application and mobile applications have become pervasive in our lives.

We are prone to adopt idioms and styles that appeal to us and shun idioms and styles that we don't like. The degree of this influence may vary widely based on the type of software we are developing versus the type of software we use as a consumer (the more alike they are, the stronger the influence). There are times when the influence may be more subconscious and other times when the influence of others' software may be obvious. In this post, I describe a recent experience with an online site that reminded me of some important software development practices to keep in mind when creating software (particularly web applications).

I was recently creating a photobook on a popular photography-related web site. I began by uploading numerous photographs for the book. The web application reported that all but one of the photographs uploaded successfully. It reported that it failed to uploaded one photograph and recommended that I verify my Internet connection. After verifying my Internet connection, I tried uploading the single photograph several more times without any success. I tried changing the name of the file and changing its file location, but still had no success. I was able to upload other photographs after those failures, but could still not upload the one particular photograph.

I decided to work on the photobook with the photographs that did upload and spent a couple hours arranging the photographs exactly the way I wanted them. When I tried to save my photobook project, however, the application would not allow me to save because it said it could not save until it finished uploading the photograph that it kept failing to upload. I clicked on the link to save the project several times without success. I could not remove the reference to the offending photograph and even the Save-As option did not allow me to save because the application thought the photograph was still uploading. Ultimately, I gave up and closed the browser, knowing that my two hours' worth of work was lost.

When I looked carefully at the characteristics of the problematic photograph, I noticed that it was exactly 1 MB (1024 KB) in size. I used some image manipulation software to make a minor change to it that affected its size (made it a bit larger) and it uploaded without incident. I had to start over, but at that point I was able to arrange (again) the photographs where I wanted them and able to save the project as desired.

As a consumer of this software, I learned a few lessons. One lesson is the need to explicitly save often when using that application because it does not have an implicit save feature and because the act of saving a project seems to be the only way to find out that the software is in an inconsistent state in which no more future work on the project will be savable. I also learned to avoid the rare case of attempting to upload an image that is exactly 1 MB to that application.

I was reminded of even more important lessons as a software developer. First, I was reminded of the importance of unit testing, especially boundary conditions. I speculate that the code used by this application looked something like this pseudo code:

if (imageSize < 1024000)
{
   // upload as-is
}
else if (imageSize > 1024000)
{
   // compress and then upload
}

In my speculative pseudo code shown above, the case of an image that is exactly 1024000 bytes leads to an image that is not explicitly uploaded. It's an easy error to make, especially if in a hurry and if no code review is performed or is rushed. Effective unit tests are perfect for driving out this type of bug and unit tests that test boundary conditions like 1024000 in this case are easy to implement. Often, just the writing of the unit test to test this boundary condition will cause the developer to realize the error of his or her ways.

Being an irritated and frustrated consumer of this software also reminded me of the importance of planning for "unhappy paths" in the software I develop. Use of this online photobook-creating software would have been less frustrating in this case if one of several options had been implemented. Had the application supported an auto-save feature that reported when it couldn't save, I'd have known that what I was working on wasn't savable. Blogger, which I used for this blog, has such a feature and when it reports to me that it cannot save, I know to stop adding new content to my post until it can save and I have a chance to copy and paste what I have typed into a file on my local hard drive.

Another option that could have saved me significant frustration would have been a Save-As feature that allowed me to save my project as a different project. I can understand the software being written to not allow me to save while it thinks it's in an inconsistent state (it thinks it's still trying to save), but it should still be able to save it as a different project. I have seen this allowed on several desktop applications that think the currently loaded document is corrupt or inconsistent but allow me to save that document anyway as a separate document (and not overwriting the previous document).

My frustration with the online photobook creation software reminded me of the importance of keeping the users' experience in mind when writing software. We can write all of the clean, readable, and maintainable software we want, but if it provides a poor user experience, that effort is for naught. This experience also was a good reminder of the importance of thorough testing, especially of "unhappy paths" and boundary conditions.

Tuesday, January 6, 2015

Book Review: Mockito Essentials

The subtitle of Sujoy Acharya's Mockito Essentials (Packt Publishing, October 2014) is: "A practical guide to get you up and running with unit testing using Mockito." The Preface and seven chapters in Mockito Essentials span approximately 190 substantive pages.

Preface

In the Preface, author Sujoy Acharya writes that Mockito Essentials "is an advanced-level guide that will help software developers to get complete expertise in unit testing using Mockito as the mocking framework." This Preface contains short summaries (typically two or three sentences) of each of the book's seven chapters.

The Preface's section "What you need for this book" lists the software needed to run examples provided in Mockito Essentials and provides links to the versions used in the book (referenced as "latest" at time of writing for some of these). These products include Mockito, JDK 7 or higher, and Eclipse (Luna 4.4.1). I would add that one also needs JUnit for most/all of the examples, PowerMock for some of the examples, and Java EE servlet JARs for some of the examples.

I quote the entire section "Who this book is for" from Mockito Essentials's Preface here because this provides a good idea of expectation of the reader and who the book in written to:

This book is for advanced to novice level software testers/developers using Mockito in the JUnit framework, with a reasonable knowledge level and understanding of unit testing elements and applications. It is ideal for developers who have some experience in Java application development as well as some basic knowledge of JUnit testing, but it covers the basic fundamentals of JUnit testing and the Mockito framework to get you acquainted with these concepts before using them.

Mockito Essentials's Preface also states that a PDF can be downloaded with versions of the book's graphics in color. I downloaded this PDF from the provided link and verified that most of the images are in color. I was also happy to see the the PDF version of the book that I reviewed already had these graphics in color. For those with printed copies of the book, however, this separate PDF with colored graphics could be helpful.

Chapter 1: Exploring Test Doubles

Mockito Essential's initial chapter, for the most part, does not cover Mockito specifically other than referencing when general unit testing practices and concepts are implemented by Mockito. Instead, the first chapter provides an overview of unit testing in general. The chapter begins with a look at why unit testing is valuable and identifies the characteristics commonly associated with effective unit tests. This short section is useful for those new to unit testing, but could probably be skipped for those familiar with unit testing concepts.

The next major section of the first chapter is "Understanding test doubles" and it's much lengthier than the first section on unit testing advantages and effective unit testing characteristics. This second section provides code listings and text explanations of the types of test doubles (term coined in XUnit Test Patterns) described in the chapter: dummy objects, stubs, spies, mock objects, and fake objects.

Chapter 2: Socializing with Mockito

Because the initial chapter of Mockito Essentials is about generic unit testing, Chapter 2 is the first chapter of the book to focus on Mockito. The chapter begins by providing links to both the Mockito main page and Wiki on github and describing Mockito and its open source (MIT) license.

Chapter 2's section on "Exploring unit test qualities" looks at "principles for readability, flexibility, and maintainability" in unit tests. Some of this content repeats ideas from the first chapter, but it's a quick section. The section "Realizing the significance of Mockito" discusses how Mockito addresses "testing-unfriendly behaviors" and interactions "with testing-unfriendly external objects" by mocking those things so the unit tests don't have to be hampered by them.

The "Working with Mockito" section of Chapter 2 starts off by displaying the Mockito logo (in color in PDF version) and then dives into specific basics of using Mockito. This section covers downloading Mockito and configuring it as a dependency in Eclipse, Maven, and Gradle. The subsection on "Stubbing method calls" provides an example of an application for testing that consists of a jQuery client that communicates with a back-end that appears to be based on Spring Web MVC. The example then demonstrates using Mockito to mock and stub classes used by the back-end class to be tested. Code demonstrates using Mockito.mock(Class) or using static imports so that it can be simply called as mock(Class). This section also introduces use of the @Mock annotation.

Chapter 2 introduces Mockito's "trigger" method when(T) along with the associated "trigger action" methods thenReturn(-), thenThrow(-), thenAnswer(-), and thenCallRealMethod(-). Chapter 2 provides an example of using a unit test method annotated with JUnit 4's @Test(expected="") along with Mockito's thenThrow method.

Mockito Essentials's second chapter illustrates use of and explains Mockito's argument matchers and references org.mockito.Matchers documentation. It then introduces ArgumentMatcher as a "Hamcrest matcher with the predefined describeTo() method" that "allows us to create our own custom argument matchers." The chapter then describes and illustrates the use of JUnit 4 with some common Hamcrest matchers such as equalTo, is, not, either, both, anyOf, and allOf.

The section in Chapter 2 called "Verifying method calls" discusses use of Mockito's static method verify to "verify the invocation" of a method on a mock object and describes situations where this might be desirable.

Chapter 2's final section ("Understanding the Mockito architecture") may have been the most (pleasantly) surprising one for me. I like the author's use of a sequence diagram to illustrate how Mockito uses CGLib (Byte Code Generation Library) to "[apply] the proxy design pattern to create mock objects." I also like that the author provides explanations and code listings that demonstrate how to "create a custom mocking framework to handle external dependencies" with Java reflection and dynamic proxies. Most readers trying to learn basics of Mockito probably don't require this knowledge, but I think it's helpful to understand the tool at the deeper level that this section provides.

Chapter 3: Accelerating Mockito

The third chapter of Mockito Essentials is intended to cover more advanced Mockito topics and begins by addressing the well-known issue of unit testing void methods (including throwing exceptions from void methods and void method callbacks). This part of the chapter also looks at doNothing(), doReturn(), ArgumentCaptor, and InOrder.

Chapter 3 features a section on "spying objects" that states, "A Mockito spy allows us to use real objects instead of mocks by replacing some of the methods with stubbed ones. This behavior allows us to test the legacy code." Text and code listings demonstrate use of Mockito's spy facility and there is a warning to use doReturn() instead of thenReturn() when working with Mockito Spy.

Chapter 3's section "Exploring Mockito Annotations" looks at three Mockito annotations such (@Captor, @Spy, and @InjectMocks). The section "Changing the default Mockito settings" describes configuration of default values returned by "nonstubbed methods of a mock object" using the five available values of the Answers enum.

Chapter 3 introduces Mockito.reset(T...) and provides a caution regarding its use similar to that in the method's Javadoc documentation. A short section of Chapter 3 covers inline stubbing. Another short section describes use of Mockito.mockingDetails (introduced in Mockito 1.9.5) to determine if an object is a mock or spy.

Chapter 4: Behavior-driven Development with Mockito

The fourth chapter of Mockito Essentials opens with the introductory sentence: "This chapter explores Behavior-driven Development (BDD) and how BDD can help you minimize project failure risks." The chapter describes top-down and bottom-up approaches and problems with each to set the context for BDD. The chapter then introduces behavior-driven development with references to Martin Fowler's TestDrivenDevelopment and domain driven design and to Agile Methods for Software Development. The chapter then references and summarizes Dan North's Introducing BDD.

After summarizing BDD, Chapter 4 moves onto "exercising BDD with Mockito." This section introduces BDDMockito and its static given(T) method. An example of using this class and method are included and the BDD-supporting Mockito syntax is briefly described.

Mockito Essentials's Chapter 4's coverage of Mockito BDD support is a relatively small part of the chapter. For developers entirely new to BDD, the entire chapter is worth reading to get an overview of the problems BDD is designed to address. For those familiar with BDD concepts already who just want to see how Mockito can be used to implement BDD testing, the last 3 pages of the chapter should be sufficient. For developers not interested in BDD, the entire chapter could be skipped.

Chapter 5: Unit Testing the Legacy Code with Mockito

Chapter 5 of Mockito Essentials begins with an introductory description of legacy code, references and quotes from the book Working Effectively with Legacy Code, and describes and why legacy code can be do difficult to work with. The chapter then describes how testing frameworks and the Java language can require developers to change otherwise good designs for testability. Given this challenge, the chapter introduces PowerMock.

Mockito Essentials's fifth chapter states, "Mockito could do the things PowerMock does, but it doesn't because those are test smells and strong indications that you are following a poor design." The author goes onto describe some of the typical ways code can be refactored to be more testable without use of PowerMock. The author then asserts, "PowerMock is a fallback for legacy code that they should aim to stop using with time." With these caveats stated, the chapter does a nice job of concisely describing what PowerMock is and how it is able to provide "its special mocking capabilities."

The fifth chapter provides links for information on PowerMock and for downloading PowerMock and then describes using PowerMockito. The chapter features several sections that describe how to apply "mocking capabilities of PowerMockito for untestable constructs" such as stubbing static methods, suppressing static blocks, suppressing a superclass constructor and class's own constructor, suppressing methods, stubbing private methods and final methods, and mocking final classes.

The section of the fifth chapter on "designing for testability with Mockito" "covers the design for testability, or rather, things to avoid in code." This section is not necessarily specific to Mockito because it covers issues common to most mocking frameworks and unit testability. This discussion is useful in terms of describing code patterns and idioms that are not mockable and presenting one or more alternatives for improving their ability to be mocked. Mockito is mentioned specifically during the discussion, but mostly to remind the reader that the code constructs to be tested need to be refactored for mocking with Mockito to be possible. It is repeatedly emphasized in this section that placing "testing impediments" inside these code constructs that are not mockable prevents them from being unit tested and moving those testing impediments to code that is mockable allows other parts to be unit tested while mocking the testing impediments instead of dealing with them directly.

Chapter 6: Developing SOA with Mockito

The sixth chapter of Mockito Essentials opens by stating that the chapter "explores web services, web service styles—SOAP-based and RESTful, web service components, and building and unit testing SOAP and RESTful web services with Mockito." The chapter begins with a brief summary of Service-Oriented Architecture (SOA) and the advantages and characteristics often associated with SOA. It moves from this brief introduction to SOA to web services with the segue that "SOA can rely on web services for interoperability between heterogeneous applications and technologies."

Chapter 6's introduction to web services presents basic characteristics of web services without distinction between SOAP-based web services and REST-based web services. It then introduces JAX-WS and JAX-RS.

Mockito Essentials's sixth chapter begins its deeper dive into SOAP-based web services by listing and briefly describing characteristics of WSDL and briefly describing the two most common approaches to building SOAP-based web services with JAX-WS (top-down/contract-first and bottom-up/Java-first). The section on JAX-WS development provides thorough coverage with text and screen snapshots how to use Eclipse with Apache Tomcat and Apache Axis to write and deploy a JAX-WS/SOAP-based web service and client. This section also describes and illustrates refactoring the code to make it more testable and then testing it and using Mockito for mocking. I have found that the tools are what making working with JAX-WS bearable, so it's not surprising that this is a tool-heavy section and one of the few sections of Mockito Essentials where Eclipse-specific behavior is significant to the narrative.

Chapter 6 also has an in-depth look at developing and testing a REST-based web service with JAX-RS. This section begins with a high-level overview of REST and major concepts that are fundamental to REST such as HTTP, URIs, HTTP status, HATEOAS, etc. Coverage then moves to "building a RESTful web service with Spring Framework." An early sentence is this section states, "This section describes the Spring MVC architecture and how RESTful web applications can be unit tested using Spring MVC." Like the section on JAX-WS, this section provides a thorough overview of developing and testing a JAX-RS/Spring-based RESTful web service using Mockito to mock certain aspects.

Chapter 7: Unit Testing GWT Code with Mockito

The final chapter of Mockito Essentials "provides an overview of Ajax/GWT, explains the Model View Presenter (MVP) pattern and loose coupling, and provides examples and strategies to mock GWT widgets using Mockito." The chapter begins with an introduction to Ajax and an example of JavaScript code using Ajax (XMLHttpRequest).

Chapter 7 describes how Google Web Toolkit (GWT) can be appealing because it hides some of JavaScript's quirks in terms of development and testing and lists several advantages of GWT. The section "Learning the MVP pattern" describes using GWT to implement an application with a Model-View-Presenter design pattern and provides background explanation regarding MVP.

Chapter 7's section "Developing a GWT application using MVP" demonstrates use of Eclipse to create a web application using Google Web Toolkit, compiling the Java code into JavaScript code, and building the overall application. This is a lengthy and detailed section that could be seen as a tutorial on using GWT. It's a completely different section, "Unit testing the GWT code," that addresses unit testing (and using Mockito) the GWT code. In addition to discussing use of PockerMockito with GWT testing, this section introduces GWTMockUtilities and GWTTestCase. I've had little exposure to Google Web Toolkit and did not realize its significant support for unit testing. I also appreciated this section's reference to HtmlUnit.

The "Summary" section of Chapter 7 is really a book summary more than a chapter summary.

General Observations

  • The code listings in the PDF version of Mockito Essentials that I reviewed are black font on white background with no color syntax and no line numbers. There is bold emphasis in many of the Java listings for Java keywords, class attributes' names, variables' names, and for literal strings.
  • Although Eclipse is the IDE used and referenced by the author, a Java developer should be able to use his or her favorite IDE. Most of the references to Eclipse are easily translated to other modern Java IDEs such as NetBeans and IntelliJ IDEA. The notable exceptions to this are the demonstrations of using Eclipse to generate JAX-WS artifacts and to generate Google Web Toolkit applications.
  • Although most of Mockito Essentials is relatively easy to read (I have included several direct quotes in this review to try to establish the relatively easy-to-understand writing style), there are some typos and significantly awkward sentences that can make a few things a bit more difficult to understand and lead me to believe another edit would have been in order. Here are some examples to provide an idea of the level of the typos and awkward sentences:
    • A method name "FindalMethodDependency" is referenced (extra lowercase "d")
    • "This is the better way is to refactor the source and make more test friendly."
    • "Building an application in an unplanned way suffers many problems, such as adding new features, making a huge effort as the architecture becomes rigid, maintaining the software (activities such as bug fixing) can turn into a nightmare, white box testing or unit testing the code becomes very difficult, and conflict and integration issues when many people work with the same or similar features."
    • "@Test(execpted=)" (a juxtaposition of characters I often type myself and the reason I left the IDE's code completion handle this one)
    • "Sometimes, we cannot unit test our code, as the special Java constructs hide the testing impediments (a LAN connection or database connection in a private method, final method, static method, or initialization block), such as private methods, final methods and classes, static methods and initialization blocks, new operator, and so on." (the constructs in parentheses are the "testing impediments" and the constructs at the end of the sentence are the "special Java constructs")
  • I liked that Mockito Essentials contains links to related tutorials, blog posts, articles, and tools' web sites. This is especially handy in the electronic edition with easy copy-and-paste.
  • Despite its title of Mockito Essentials, this book covers more than Mockito essentials.
    • It provides relatively substantial introductions to SOAP-based and REST-based web services and to developing web applications with Google Web Toolkit and Spring MVC. Although these lengthy introductions ultimately lead to some discussion about unit testing and mocking those types of applications, there is a significant amount of time spent on the development before even getting to the testing. This can be seen as a positive or a negative depending on the reader's perspective. If a potential reader knows very little about one of these, he or she might appreciate the significant background. If a reader already knows these well or doesn't care to learn them, these sections are probably extraneous.
    • For the reader interested in "core Mockito," the most interesting chapters of Mockito Essentials will be Chapter 1, Chapter 2, Chapter 3, and Chapter 5. Chapter 1 is more general than simply Mockito and provides good background for those new to mocking and Mockito and could probably be skipped by someone with basic familiarity with mocking and Mockito.
    • Chapter 4 will be of most interest to those practicing behavior-driven development (BDD) or interested in learning more about it and potentially practicing it. It's a relatively short chapter and provides an interesting BDD discussion and practical examples.
    • Chapter 6 will be of primary interest to those developing and testing web services (SOAP-based or REST-based). My guess is that most developers interested in this chapter will already be familiar with JAX-RS or JAX-WS and won't need the detailed introduction to generation of web services with those APIs, but that introductory information might be useful to the reader with less familiarity who wants a taste of web service development in Java.
    • Chapter 7, like the previous chapter on web services, is going to be of most interest to developers who use Google Web Toolkit. As with the web services chapter, the extensive description of generating a GWT-based application is probably not necessary for most of those folks.
    • I liked some of the broad categories and topics covered in Mockito Essentials, but think it's important to emphasize that the book, at times, is broader than Mockito. Although this is always done with the intent of providing examples of using Mockito to mock portions of the generated examples, there are chapters where the general development discussion is longer than the unit testing discussion.
  • I am comfortable recommending Mockito Essentials for the developer who wishes to learn more about basics and uses of Mockito. The most significant caveat for me in making this recommendation is that Mockito provides outstanding online documentation and many of Mockito's most common use cases are well described on its API page (contains 22 code snippets and descriptions as of this writing).

Conclusion

Mockito Essentials covers the basics of Mockito and presents some realistic examples of how Mockito can be used to mock portions of Java-based applications that would otherwise violate fundamental tenets of unit testing and make unit tests less effective. Mockito Essentials provides detailed examples of applying Mockito with other tools and frameworks such as PowerMock, Google Web ToolKit, JAX-WS, and JAX-RS. Along the way, many of the commonly accepted practices for writing effective tests and for effective mocking are introduced and explained.

Saturday, February 15, 2014

Book Review: Test-Driven Development with Mockito

Test-Driven Development with Mockito provides a general introduction to test-driven development (TDD) before looking at application of Mockito to implement test doubles as part of test-driven development. Test-Driven Development with Mockito is written by Sujoy Acharya, is published by Packt Publishing with a publication date of November 2013, and has about 150 substantive pages of substantive content divided into 9 chapters and 2 appendices. The subtitle of Test-Driven Development with Mockito is "Learn how to apply Test-Driven Development and the Mockito framework in real life projects, using realistic, hands-on examples."

Preface

Test-Driven Development with Mockito's Preface opens with a very brief description of test-driven development and how Mockito plays a role in test-driven development. The preface summarizes each chapter and appendix before recommending what a reader of that book should use while reading the book: J2SE 5, the Eclipse IDE (Kepler 4.3 recommended), and Mockito.

One of the most important parts of a Packt Publishing Preface is the section on "Who this book is for." In this case, the paragraph in this section states:

This book is for developers who want to develop software according to Test Driven Development using Mockito and to leverage various Mockito features. Developers don't need prior knowledge of TDD, Mockito, or JUnit. It is ideal for developers who have some experience in Java application development as well as some basic knowledge of unit testing, but it covers the basic fundamentals of TDD and JUnit testing to get you acquainted with these concepts before you use them.

Chapter 1: Getting Familiar with TDD

As the Preface stated, Test-Driven Development with Mockito does cover "basic fundamentals of TDD" and begins this in the first chapter. This chapter starts out at a very basic level, defining a test in Java development and listing some "available code-driven unit-testing frameworks for Java." In this chapter, we learn that JUnit 4.0 will be used in examples in the book. An example is then presented using Eclipse and JUnit in which the test class is written before the production class. This transitions into more details on the historical background of TDD and lists benefits of TDD.

Chapter 1 continues by looking into how TDD fits into overall software development and how refactoring plays a role in test-driven development. In many ways this first chapter is what you'd expect from a book on test-driven development. The code-based examples of test-driven development do a nice job of illustrating basic techniques and advantages associated with test-driven development. However, there seemed to be some things that seemed a bit out of place in this chapter. In particular, the chapter's extremely brief coverage of "Object-function programming languages such as Scala and Groovy" seemed irrelevant to the rest of the chapter and added little value. As with most books on TDD, this chapter of this book had a paragraph that seemed a bit preachy.

Chapter 2: Refactoring – Roll the Dice

The second chapter of Test-Driven Development with Mockito, as its name implies, focuses on refactoring. The chapter begins by reviewing benefits of refactoring and then discusses "trigger points for refactoring" and when not to refactor. Some relatively lengthy code listings are provided to show some code in desperate need of refactoring with text explaining issues with the code and possible ways to refactor that code. This section of the book also introduces the Eclipse plugin CodePro Analytix.

The second chapter moves onto coverage of code smells. Duplicate code with switch statements is shown as a code smell and there is discussion and more code listings on how to refactor this using the strategy pattern and polymorphic behavior. Other code smells that are covered in this chapter along with potential remedies include duplicate code, comments, long methods, long method parameter lists, large classes, dead code, and overengineering.

This second chapter is a reasonable introduction to code smells and how to use refactoring and other techniques to deal with them. This chapter mentions common object-oriented and software development principles such as YAGNI, DRY, open-closed principle, and strategy pattern. A single chapter of a book cannot compete with an entire seminal treatment of a subject, but this chapter does provide a good illustration of how refactoring fits into TDD.

Chapter 3: Applying TDD

The third chapter of Test-Driven Development with Mockito begins with a bold statement, "Test-Driven Development (TDD) is the new way of programming." The chapter then outlines the simple steps that are taken to implement TDD and is the first chapter in the book to really dive into TDD. The chapter concludes with a lengthy example including code listings and Eclipse screen snapshots to demonstrate JUnit and TDD in practice.

Chapter 4: Understanding the Difference between Inside-out and Outside-in

Chapter 4 of Test-Driven Development with Mockito compares and contrasts TDD's two "common" styles: Outside-In and Inside-Out. The chapter provides details regarding the outside-in approach before listing advantages and disadvantages of that outside-in approach. The same is then done for inside-out, though the coverage is much shorter than for outside-in.

Chapter 5: Test Doubles

Test-Driven Development with Mockito's fifth chapter covers test doubles. The chapter very briefly discusses why test doubles are useful (I don't think this part is particularly strong) before categorizing the types of test doubles (dummy, stub, mock, and fake). The coverage of these types of test doubles is much better than the general introduction to test doubles and provides nice code examples and discussion on each and how they are different.

Chapter 6: Mockito Magic

Chapter 6 of Test-Driven Development with Mockito is the book's first significant look at Mockito (although Mockito was mentioned in Chapter 5 for the example of the mock category of test double). The chapter begins with an overview of Mockito and explains Mockito's primary roles in test-driven development: "Mockito plays a key role to mock out external dependencies" and helps achieve the desired "qualities of unit testing."

Chapter 6 continues by demonstrating adding Mockito to Eclipse. There are numerous screen snapshots of Mockito being used within Eclipse (these are in color in the electronic edition of the book). The chapter introduces key annotations and functionality provided by Mockito and demonstrates using Mockito to address things commonly tested using mocks. A reader of this book who's primary interest in the book is learning Mockito would find this to be one of his or her favorite chapters of the book.

Chapter 7: Leveraging the Mockito Framework in TDD

Test-Driven Development with Mockito's seventh chapter begins with a brief discussion of classical TDD versus mockist TDD. The chapter is then introduced with the statement that it will "use Mockist TDD style to mock external dependencies and explore Test-Driven Development." The entire chapter is then devoted to a fairly realistic example of building an "online portal for the aspiring game developers" with test-driven development techniques and using Mockito to mock out the external interfaces.

Chapter 8: World of Patterns

Chapter 8 of Test-Driven Development with Mockito opens with reference to Robert Martin's "three important characteristics of a bad design": rigidity, fragility, and immobility. The chapter then covers "higher level design guidelines" of modularity, high cohesion, and low coupling and "lower level design principles" of Open/Closed Principle, Dependency Inversion Principle, Integration Segregation Principle, Single Responsibility Principle, Liskov Substitution Principle, and the Law of Demeter.

The next section of Chapter 8 discusses design patterns. The section provides an overview of what design patterns are, how they are categorized, and provides a couple of examples of applying design patterns.

Chapter 9: TDD, Legacy Code, and Mockito

The ninth chapter of Test-Driven Development with Mockito discusses what constitutes legacy code and then emphasizes: "Any code with no unit test is legacy code." Chapter 9 looks at some common less-than-testable features of legacy code and how to refactor that code for greater testability. The chapter then moves onto how Mockito can make it easier to refactor code to be more testable.

Appendix A: TDD Tools and Frameworks

Appendix A covers Eclipse IDE and JUnit 4 unit testing framework. It discusses using keyboard shortcuts in Eclipse (including "The mother of all shortcuts is Ctrl + Shift + L" to see all shortcuts) and emphasizes those shortcuts useful in refactoring and TDD. There are some nice color screen snapshots showing shortcuts applied in this section. Appendix A's coverage of JUnit begins with the statement, "Inheritance in Java is not a smart thing to implement" to explain advantages of JUnit 4.x (which supports annotations) over earlier versions which required inheritance. Appendix A's coverage of JUnit covers fundamentals of JUnit and demonstrates using Eclipse shortcuts in JUnit unit test development.

Appendix B: Agile Practices

Appendix B discusses continuous integration (CI) in general before covering Jenkins as a particular continuous integration tool. The author begins continuous integration coverage with the statement, "In a good software development team, we'd find TDD as well as CI." There are numerous color screen snapshots depicting configuration of Jenkins. Appendix B also discusses the "Agile development methodologies Scrum and Kanban." After briefly discussing Agile in general and providing a reference to the Agile Manifesto, Appendix B focuses on Scrum and Kanban specifically.

General Observations
  • Test-Driven Development with Mockito attempts to cover numerous topics within its roughly 150 pages.
  • Test-Driven Development with Mockito has several nice images. I especially like the screen snapshots that depict Eclise refactoring keyboard shortcuts and the screen snapshots that depict Jenkins configuration. An advantage of my reviewing the PDF provided by Packt Publishing is that these images are in full color.
  • The code listings are useful in illustrating the points being made, but the lack of color syntax (even in the electronic PDF version) make it more difficult to read the code quickly and easily. I have become spoiled by my IDE (and even my vim!) and online code listings featuring color syntax. Reading code without color syntax reminds me of the "old days," but the nostalgia is not worth the extra effort required to read the code.
  • Test-Driven Development with Mockito has some typos in it and also has some awkward and incomplete sentences. However, none of these were significant enough to prevent me from picking up the concept being covered.
  • Although the flow of topics covered in Test-Driven Development with Mockito generally made sense, there were some aspects of the arrangement of the various concepts that were a bit awkward. In particular, there were a couple concepts that were presented in almost a "side note" manner at first and were not very clearly explained, but then were much more clearly explained in the later sections that focused on that topic.
  • As is the case with just about every TDD-related book I have read, Test-Driven Development with Mockito is a highly opinionated book. Although I generally agreed with most of the opinions, I felt some of the stated opinions were backed up better than others and some I don't feel quite as strongly about or think are quite as certain as the author does. That being stated, I do like to see opinions expressed and I generally agreed with them in principle. The more focused books I mentioned in an earlier bullet would be useful for the reader of Test-Driven Development with Mockito who wanted to better understand the "why" behind some of the opinions expressed in this book.
Conclusion and Recommendation

Test-Driven Development with Mockito will be most beneficial to readers who have little to no familiarity with the numerous concepts introduced in the book (test-driven development, refactoring, object design, JUnit, Mockito, etc.). The book provides an overview of each of these and how they are used together. Developers who have read other books on these subjects may not find the introductory information in Test-Driven Development with Mockito as useful or as new to them. Developers wanting in-depth coverage of a topic might prefer a book focused on that particular topic. The book is most useful for those using the Eclipse IDE, but many of the sections are useful for developers who don't use Eclipse.

Monday, September 2, 2013

Better JUnit-based Unit Tests with NetBeans 7.4 beta Hints

In my last post, I wrote about hints provided in NetBeans 7.4 beta that improve a developer's ability to avoid nasty runtime issues with Java exception handling. In this post, I look at how two more hints provided by NetBeans 7.4 beta can be used to make unit tests more correct and more clear during unit test execution. These are the "Inconvertible parameters of Assert.assertEquals" and "Incorrect order of parameters of Assert.assertEquals" hints.

As far as I can tell from anecdotal evidence and from talking to other Java developers, JUnit remains the most widely used unit testing framework in the Java environment. Most of these JUnit users are intimately familiar with JUnit's Assert class and its many overloaded assertEquals methods. NetBeans 7.4 beta now provides two hints to make it easier to use these assertEquals methods appropriately.

Although many of the Assert.assertEquals() methods have very specific data types for the "expected" and "actual" parameters to be asserted as equal, there is a version that accepts two Objects and this means two parameters of different types that cannot possibly be considered "equal" can still be passed to that method. There is no way for the compiler to prevent that, but NetBeans 7.4 beta includes the "Inconvertible parameters of Assert.assertEquals" hint to address that particular case. Without such a hint, one is more likely to not realize the error of his or her ways until he or she runs the JUnit test and sees the failed assertion.

One of the most common issues I've run into with JUnit (and one of the reasons I love Hamcrest's fluent API) is that I just cannot seem to remember with confidence which order the parameters to the assertEquals methods are. I have a 50/50 chance of being correct by guessing. Modern Java IDEs such as NetBeans help greatly when writing new JUnit code because their method completion features will indicate that the "expected" parameter is specified first and the "actual" parameter is specified second. More commonly, this is a problem when reading code rather than when writing it because there is no method completion helping me read the code. NetBeans 7.4 beta addresses this by highlighting the situation in which I have mixed up the parameters' order via the "Incorrect order of parameters of Assert.assertEquals" hint. With that hint enabled (which it is by default), I can quickly recognize out-of-order parameters prior to runtime and even without method completion.

Both of the hints discussed above can be demonstrated in a very simple unit test class.

Portion of CalculatorTest.java
/**
 * Test Calculator.sum(int ...).
 */
@Test
public void TestSumIntegers()
{
   final Calculator calculator = new Calculator();
   Assert.assertEquals(calculator.add(1, 2, 3, 4, 5), 15);
   Assert.assertEquals("15", calculator.add(1, 2, 3, 4, 5));
}

The code the above unit test method is testing is not important for this discussion. Rather, the focus is on the use of Assert.assertEquals in two cases. Both cases as shown above are incorrect and force demonstration of the two previously discussed NetBeans hints. The first attempt at asserting two objects are equal places the parameters in the wrong order. The "expected" value (hard-coded 15) should be listed first followed by the "actual" value calculated by the method under test. The second attempt at asserting two objects are equal will always fail because the types don't match: the first parameter is a String and the second parameter is an integer. In both of these cases, the unit test code compiles without complaint. However, both assertions will ALWAYS fail when running the unit tests. In fact, these tests results may inadvertently be interpreted as problems with the code being tested until someone looks at the test failures in greater depth.

The next two screen snapshots demonstrate NetBeans 7.4 beta flagging both problematic unit testing assertion statements.

There is one caveat to note regarding the "Incorrect order of parameters of Assert.assertEquals" hint. It works well when the assertion statement is like the one shown in my example: a hard-coded expected value is provided as the "actual" value along with an obviously calculated value as the "expected" value. The next screen snapshot illustrates this point; only the statement I earlier showed is flagged by the hint and other ways of comparing actual to expected are not flagged, even when the order is incorrect.

The last shown screen snapshot demonstrates that the NetBeans hint is only able to detect an incorrect order of assertEquals parameters (should be expected before actual rather than actual before expected) in the case where those values are directly accessed in the statement (the actual calculation is performed for the first [expected] parameter and the expected hard-coded value is provided for the second [actual] parameter).

The two hints covered in this blog post make it easier to detect problems with the frequently used JUnit Assert.assertEquals methods that might not be detected until analysis of unit test run results without the hints. Although the two issues these hints warn developers about are typically fairly easy to detect and fix, detecting and fixing these problems is still more difficult and time-consuming than having the NetBeans IDE tell you they are wrong before you even run the test.

Wednesday, August 28, 2013

Book Review: TestNG Beginner's Guide

I was happy to accept Packt Publishing's invitation to review the recently released (July 2013) book TestNG Beginner's Guide because I welcomed the opportunity to learn more about TestNG. I have read about and played a little bit with TestNG before, but the vast majority of my Java unit testing experience has been with JUnit.

Author Varun Menon opens TestNG Beginner's Guide with the following statement in the Preface, "Currently, TestNG is the most widely used testing framework in the software industry." This is definitely not what I've found in my experience where JUnit has been used far more than TestNG in the different Java projects that I've worked on. The Preface lists four things needed for getting most use out of TestNG Beginner's Guide: Java JDK, Eclipse, Linux or Windows, "basic knowledge of Java and testing."

Chapter 1: Getting Started

The first chapter of TestNG Beginner's Guide opens with a brief introduction to testing and automated testing. The chapter then introduces TestNG and provides a brief history and overview of the features of that open source unit testing framework. The chapter describes how to acquire TestNG via download (testng-6.8.jar in the book) and explains five ways it can be run: command line, Eclipse plugin, IntelliJ IDEA plugin, Ant, and Maven. The chapter spends multiple pages and several screen snapshots demonstrating configuration of TestNG within Eclipse via the plugin update site http://beust.com/eclipse. It demonstrates creating an Eclipse project, associating the TestNG library with the Eclipse project, writing a TestNG-based test class, and executing the TestNG test class in Eclipse. There are numerous screen snapshots in this Equinox-heavy portion of the chapter. Similar instructions on using TestNG with Eclipse can be found in the TestNG Eclipse documentation. The TestNG documentation includes a section on running TestNG from the command line.

Chapter 2: Understanding testng.xml

The second chapter of TestNG Beginner's Guide focuses on the testng.xml file, which it describes as "a configuration file for TestNG" that is "used to define test suites and tests in TestNG" and is "used to pass parameters to test methods." The chapter focuses on using testng.xml to specify test suites. It covers use of TestNG within Eclipse and from the command line. This chapter contains some of the concepts central to using TestNG. The examples shown are Eclipse-oriented, but XML snippets are shown as well, which is useful for those not using Eclipse. As testng.xml is a core part of TestNG, it is not surprising that Chapter 2 is a core part of TestNG Beginner's Guide.

Chapter 3: Annotations

The third chapter of TestNG Beginner's Guide, like the second chapter, focuses on a core part of TestNG. In this case, it's on TestNG's annotations. The chapter introduces Java annotations and standard Java annotations (@Override, @Deprecated, and @SuppressWarnings) before covering TestNG's specific annotations in a table with annotation name and brief description.

Chapter 3 of TestNG Beginner's Guide examines TestNG's annotations in more detail after the initial listing of them in the table with annotations names and descriptions. This more detailed examination includes code uses of these annotations. Annotations covered in this chapter include the @Test annotation (including specification of timeouts and expected exceptions), several of the wide variety of "before" and "after" TestNG annotations (such as @BeforeClass and @AfterMethod), @Optional, @Parameters, and @DataProvider. This, along with Chapter 2, is another "core" chapter of the book because annotations are core to TestNG. The third chapter also includes additional references to testng.xml.

Chapter 4: Groups

The fourth chapter of TestNG Beginner's Guide covers TestNG Groups. Coverage of test groups covers grouping of tests, running those groups of tests, specifying tests as parts of more than one group, using regular expressions to specify which groups to run, and grouping test groups within groups (metagroups). As with many of the other chapters, this chapter includes numerous screen snapshots showing Eclipse used with the different concepts, but code snippets are also included.

Chapter 5: Dependencies

TestNG Beginner's Guide's fifth chapter is on dependencies, a TestNG mechanism that allows tests to be executed in a particular order via expression of dependencies between tests and other tests or groups of tests. Screen snapshots of Eclipse and code listings are used to illustrate creating a dependency between one test on another, between one test on multiple other tests, between one test and a test in another class in the same inheritance hierarchy, and between a test and a group. The chapter also covers use of regular expressions with test dependencies and using XML to specify test dependencies.

Chapter 6: The Factory Annotation

The sixth chapter of TestNG Beginner's Guide introduces TestNG factories. The chapter explains the use of factories to "define and create tests dynamically at runtime." Through more screen snapshots of Eclipse and mode code listings, the chapter introduces the @Factory annotation and shows how it is used. This chapter also introduces the @DataProvider annotation and demonstrates using these two annotations together. The chapter also includes explanation of how @Factory and @DataProvider differ and when each is most appropriate.

Chapter 7: Parallelism

The seventh chapter of TestNG Beginner's Guide covers parallelism, or running tests with multiple threads. The chapter includes a brief section describing two reasons why being able to run tests in parallel can be advantageous.

Chapter 8: Using Build Tools

The eighth chapter of TestNG Beginner's Guide begins by outlining the "advantages of automating a build" and highlights Ant, Maven, and Gradle as the "major build tools that are being used by the software industry when it comes to Java or Java related languages." The rest of the chapter focuses on incorporating TestNG with Ant and with Maven using TestNG's custom Ant task and Maven plug-ins.

Chapter 9: Logging and Reports

Chapter 9 of TestNG Beginner's Guide introduces TestNG support for reporting and logging with an introductory paragraph on how reporting and logging are generally beneficial. The chapter introduces the interfaces ITestListener and IReporter and very briefly differentiates between these and where each is more appropriate. Like several other chapters, the ninth chapter mixes screen snapshots of Eclipse with code listings to demonstrate how to write a custom logger implementing the ITestListener interface, how to write a custom reporter implementing the IReporter interface, using TestNG-provided XML and HTML reports, and generating a JUnit HTML report with Ant. This chapter also introduces ReportNG, which is described on its website as "a simple HTML reporting plug-in for the TestNG unit-testing framework" that "is intended as a replacement for the default TestNG HTML report." This chapter also introduces Reporty-ng, formerly known as TestNG-XSLT.

Chapter 10: Creating a Test Suite through Code

The tenth chapter of TestNG Beginner's Guide covers "how to configure and run [a] test suite through code" to help "in configuring and running tests at runtime." In short, this chapter covers how to run TestNG tests programmatically rather than via specification in a testng.xml file. The chapter introduces and describes "the API classes" XmlSuite, XmlTest, XmlClass, XmlPackage, XmlDependencies, and a few more. The chapter adapts examples that were used earlier in the book to demonstrate using testng.xml for configuration but instead specify configuration in the code itself.

Chapter 11: Migrating from JUnit

The eleventh chapter of TestNG Beginner's Guide was one that I most looked forward to as I scanned the table of contents of the book. I find documentation on migration of one product (or version) to another product (or version) useful for two reasons. First, if I know the product or version being migrated from, I can better understand how the product or version being migrated to implements the same functionality. It helps me to learn the new product or version. Second, if I decide to adopt the new product or version, the migration guide is helpful in transitioning to that new product or version.

The section of the TestNG documentation called Migrating from JUnit states, "TestNG can automatically recognize and run JUnit tests, so you can use TestNG as a runner for all your existing tests and write new tests using TestNG." TestNG Beginner's Guide begins Chapter 11 with coverage of executing JUnit tests in TestNG. The chapter continues with a demonstration of running JUnit and TestNG tests together via testng.xml specification and then demonstrates "running JUnit tests along with TestNG [tests] through Ant."

Chapter 11 also covers true migration of JUnit-based unit tests to TestNG-based unit tests (as opposed to simply running JUnit-based tests in TestNG). This section includes an extremely handy table that compares the feature of TestNG to use compared to the feature of JUnit 3 and JUnit 4 to fulfill the same use case. This table is one of my favorite features of TestNG Beginner's Guide. The table validated my own internal mapping that I had been creating in my mind of TestNG concepts to JUnit concepts. The chapter also discusses how assert statements are handled differently in JUnit versus TestNG and this reminded me of why I prefer Hamcrest-based assertions with their high readability over straight JUnit assertions in which I must remember which of the two is an expected value and which is the actual value.

Chapter 12: Unit and Functional Testing

The final chapter of TestNG Beginner's Guide focuses on using TestNG for unit and functional testing. The chapter provides basic definitions of unit testing and functional testing before examining each approach in more detail via examples.

The unit testing section of Chapter 12 provides a table of assertions supported by TestNG and demonstrates use of an assertion in a code sample. That section also focuses on mocking with TestNG. Although this chapter specifically mentions jMock, Mockito, EasyMock, PowerMock, and JMockit as a "few of the mocking utilities," the chapter's focus is on jMock and Mockito.

After contrasting functional testing against unit testing, Chapter 12 includes a "list of functional testing automation tools that are available in the market" including Selenium, Sikuli, and SilkTest. Selenium is the product in the functional test coverage that gets the same focus that jMock and Mockito got in the unit test coverage.

The final paragraph of the Summary of Chapter 12 is also the summary paragraph for the entire book.

Overall Positives
  • For Beginners - As its title TestNG For Beginners correctly advertises, this book is aimed at beginners. I found my experience with JUnit to make it very easy to understand the concepts covered in this book, but I think most Java developers, even with less JUnit experience, would find this book highly approachable.
  • Screen snapshots and Images - TestNG Beginner's Guide features numerous screen snapshots that can be helpful in learning how to use TestNG, particularly in conjunction with the Eclipse plugin. The one downside is that many of the images are Eclipse-specific. This is a positive for Eclipse users, but may be a bit of a negative for users of other IDEs.
  • Code Listings - TestNG Beginner's Guide includes numerous code examples and listings. Because they are listed for nearly every covered topic, users of IDEs other than Eclipse should still be able to use this book.
Overall Negatives
  • Editing and Polish - One of the weaknesses of several (not all) of the Packt titles I have reviewed has been occasional convoluted sentence structure and weird grammar and incorrect spelling. This particular book has some strange grammar in a couple places as well as several spelling errors. Examples of spelling errors include @SupressWarnings (only one "p") and "TestNg" rather than "TestNG" on the same page 52. The good news is that these are mostly just minor distractions or annoyances and rarely take away from understanding of the meaning.
  • For Beginners - This is an advantage for new Java developers and developers new to unit testing, but is a bit of a disadvantage for more experienced developers (especially with JUnit). The book does a sound job of covering the most important features of TestNG, but there is very little coverage of why these features are useful. The simplicity and clarity of the examples comes at the price of the examples not providing very realistic ideas of how these TestNG features might be used in real code.
  • Eclipse-centric - The book provides enough code listings to largely overcome this limitation and this is not a limitation, of course, for Eclipse users.
Other Observations

The title says it all: TestNG Beginner's Guide is for those new to TestNG.

As already mentioned this book is targeted most closely at Eclipse users. That stated, I think developers with at least some Java experience will find it easy to pick up the concepts and even implement the code listings in their favorite IDE even when that IDE is not Eclipse. Complete code examples are typically provided and most of the many Eclipse screen snapshots are showing reported results which will be shown on the command line or in other IDEs.

The ordering of the chapters is not the same order I'd have chosen, but this is not a big deal. For the most part, one could read the chapters in the order one prefers or even skip chapters on subjects that are not of interest or which the reader is already familiar. The chapters rely slightly on each other, but for the most part could be read fairly independently of one another. There are references to earlier parts of the book in several chapters, but these references are often not necessary to understand the newly covered topic.

Most of the material covered in TestNG Beginner's Guide is covered online in TestNG documentation and in blog post. I have linked to specific sections or pages of these resources several times in my review of this book. Cédric Beust's blog is another obvious source of information on TestNG. The TestNG site includes the More TestNG Reading page with links to numerous additional online TestNG resources. Although there is little in TestNG Beginner's Guide that is not available online, this book provides organization for the introductory material and can provide context that is especially helpful when accessing seemingly unrelated blog postings.

TestNG Beginner's Guide is close to 250 pages of content plus more pages for table of contents, index, and so forth. Much of the space on these pages consists of code listings and screen snapshots, so the overall prose content is well below 250 pages. The examples are very simple, making it easy to focus on the TestNG feature being demonstrated, but also meaning that more subtle understanding of when certain features would be useful might be missed, especially by those not familiar with unit testing.

Conclusion

TestNG Beginner's Guide is squarely aimed at developers new to TestNG. Its twelve chapters cover the basics of TestNG and are approachable for anyone with some basic Java ability, but are especially easy to understand with some Java and JUnit experience. The book is probably too introductory for advanced TestNG users, but its title suggests that this is intentional. The only caveat I'd offer to a person new to TestNG who is trying to decide whether to obtain this book is to consider whether the book helps fill a need that is not covered adequately by the broad TestNG documentation or blog posts such as those by mkyong. I personally find some advantages to learning from books in conjunction with online resources, but I understand that this is a matter of taste.

Friday, April 12, 2013

Book Review: Effective Unit Testing: A Guide for Java Developers

In the Preface for Effective Unit Testing: A Guide for Java Developers, author Lasse Koskela states that although the impetus for Effective Unit Testing was to "write a Java edition of Roy Osherove's book, The Art of Testing with Examples in .NET," Effective Unit Testing ended up "having very little in common with Roy's book." Koskela further explains in the Preface that "this book is for the Java programmer," but adds that "writing good tests is a language-agnostic problem" and he recommends his book even for developers using languages other than Java.

Koskela does a nice job in the "Preface" of succinctly summarizing what the book is and isn't when he says, "I didn't want to give you a tutorial on JUnit or my favorite mock object library" and "I've tried to minimize the amount of technology-specific advice." This is important to note because Effective Unit Testing is not the book you'll want if you're looking for a book covering intricate details of JUnit, TestNG, Mockito, EasyMock, Hamcrest or other commonly used Java unit testing frameworks and tools. Instead, the focus of Effective Unit Testing: A Guide for Java Developers is on more general concepts of unit testing in general (and in Java in particular) that might be implemented with any variety of different tools. That being stated, Koskela doesn't ignore these tools completely and does sprinkle JUnit code throughout the book along with code samples based on other unit testing tools such as JMock, Mockito, and Hamcrest.

Chapter 1: The Promise of Good Tests

In Chapter 1 of Effective Unit Testing, Koskela mixes brief historical testing anecdotes with basic introductory material and often-cited reasons on why unit tests and automated tests are important (identifying bugs, improving design, avoiding scope creep, and learning from the test-writing experience). Koskela talks about why units tests are more effective when used for design in addition to being used for quality assurance.

The "Factors of Productivity" section of Chapter 1 is useful for understanding certain measures by which one might determine whether unit tests are "effective." These include execution speed (performance), readability, reliability, and trustworthiness. Achieving these characteristics of effective unit tests is the focus of the book. A couple concluding sections of the first chapter focus on using tests for design and employing behavior-driven development (BDD).

Among other introductory details, he articulates why "100% code coverage isn't the goal." Several well-known testing-related terms ["test-infected", "test-driven development" (TDD), and "accidental complexity"] are also introduced in this chapter along with references for additional details. In this first chapter, the author also introduces his "The Law of Two Plateaus" to differentiate between using unit tests solely for quality assurance versus using them for design in addition to quality assurance.

Chapter 1 is mostly introductory and probably doesn't hold a lot of new insights for someone who has worked with Java extensively and/or has written unit tests extensively. However, it does manage in 12 pages to meet the author's goal for it and the other two chapters (Chapter 2 and Chapter 3) of Part 1 of providing a "shared context" for the remainder of the book.

Chapter 2: In Search of Good

Chapter 2 delves deeper into the question of "What makes a test 'good'?" Koskela is quick to point out that there is some subjectiveness to this ("some of the quality of test code is in the eye of the beholder" as is the case for any code) and that different contexts can affect whether a particular unit test is good or not.

Koskela discusses in this chapter how virtues of regular source code are often valued virtues of test code. For example, he discusses that readable test code is maintainable test code and that appropriate structure plays a big role in making tests understandable. Koskela devotes a section of Chapter 2 to a smaller but significant issue I've seen repeatedly in writing and maintaining my own and others' unit tests: unit test methods that advertise testing something they don't really test (perhaps because they are named poorly) can be very costly. I like what Koskela titles that section, "It's not good if it's testing the wrong things." This sounds obvious, but there is deep truth to that simple statement.

Another section of the second chapter focuses on the principle that, for good tests, "independent tests run easily in solitude." Koskela provides a list of dependencies (such as "randomness" and "persistence") that to him are code smells indicating that something might be wrong with the unit test code. I like his "litmus test for a project's test infrastructure" to satisfy the following scenario: "Can I check out a fresh copy from version control to a brand new computer I just unboxed, run a single command, lean back, and watch a full suite of automated tests run and pass?" This section does a nice job of covering why it's important that tests are independent and do not rely on being called in a certain order. In addition to pointing out some unit test dependency smells in this section, Kosekla also provides some specific approaches that might be taken to address these.

One of the sections of the second chapter of Effective Unit Testing looks at why testing the wrong thing or even testing nothing at all ("happy tests") are problematic. There is coverage of why tests "need to be repeatable" along with references to Java-specific examples of tests that introduce things outside of the testing developer's control into the tests.

The last section of Chapter 2 (not counting the "Summary") introduces test doubles, the subject of Chapter 3. Koskela defines test doubles as "an umbrella term for ... stubs, fakes, or mocks." He adds that "test doubles" are "objects that you substitute for the real implementation for testing purposes." Koskela groups test doubles with testing frameworks and build tools as his "top three tools of the trade for software developers writing automated tests."

Chapter 3: Test Doubles

The third chapter is the concluding chapter and my favorite chapter of Part 1 ("Foundations"). The chapter is devoted to coverage of "test doubles", a term and concept introduced in Gerard Meszaros's xUnit Test Patterns: Refactoring Test Code. Koskela outlines five reasons developers might use test doubles, including "the most fundamental of the reasons for employing a test double - to isolate the code you want to test from its surroundings." After listing these five reasons for use of test doubles, Koskela describes each of these motivating reasons in greater detail. He then describes each of the types of test doubles and compares their strengths and weaknesses. Koskela's section "Guidelines for Using Test Doubles" introduces his "logic and heuristics" for "picking the [test double] option that results in the most readable test". These include five considerations plus the simplifying rule: "stub queries; mock actions" (attributed to J.B. Rainsberger, author of JUnit Recipes).

The third chapter of Effective Unit Testing also touches on organizing unit tests with the Arrange-Act-Assert convention and likens this to behavior-driven development's Given-When-Then vocabulary. This chapter also demonstrates principles of the chapter with brief forays into JMock and Mockito code examples and a reference to J.B. Rainsberger's blog post JMock v. Mockito, but Not to the Death.

Chapter 4: Readability

The fourth chapter of Effective Unit Testing is the first chapter of Part 2 ("Catalog"). As with all three chapters in Part 2, Chapter 4 looks at "test smells" that might indicate tests that are less effective than they could be. In this chapter's case, the test smells are those most closely associated with problems related to readability of unit tests.

Koskela starts Chapter 4 by articulating the difference between reading test code and running test code: "Reading the tests ... should provide the programmer with an understanding of what the code should do. Running those tests should tell the programmer what the code actually does."

The test smells that Koskela associates most closely with the "readability" portion of his Test Smells Catalog are:

  • Primitive Assertions
    • Assertion that "uses more primitive elements than the behavior it's checking"
    • Analogous to the primitive obsession code smell
  • Bitwise Assertions
    • Special case of Primitive Assertions that uses bitwise operators for "optimized test assertions" at the cost of readability and understandability
  • Hyperassertions
    • Assertion that that "becomes brittle and hides its intent under its overwhelming breadth and depth"
  • Incidental Details
    • Incidental details make it difficult to identify the "intent, purpose, and meaning" of a unit test
  • Split Personality
  • Split Logic
    • Test code scattered over multiple files
  • Magic Numbers
    • Using numeric and String literals rather than using constants and variables with readable names
  • Setup Sermon
    • Too much code (often refactored from tests suffering incidental details smell) in the setup method
  • Overprotective Tests
    • Application of unnecessary/redundant guard or test assertions when condition would fail anyway

In each test smell case, Koskela provides examples of these test smells along with one or more ways (description and code examples) of addressing the smells.

Hamcrest is introduced in Chapter 4 as a partial solution to addressing test smells. There is also a unit test example that is built for testing JRuby source code.

In the fourth chapter, Koskela provides some memorable quotes. He articulates an opinion that I've long held regarding unit test code: "When weighing alternatives to expressing intent in test code, you should keep in mind that the nature and purpose of tests puts a higher value on readability and clarity than, say, code duplication or performance." He also writes, "A test that has never failed is of little value - it's probably not testing anything. On the other end of the spectrum, a test that always fails is a nuisance." Koskela also explains that "A test should have only one reason to fail" and explains that this is related to the Single Responsibility Principle.

Chapter 5: Maintainability

Chapter 5 continues the coverage of test smells, but moves from the focus of Chapter 4 on "readability" to focus instead of "maintainability." As he did in Chapter 4, Koskela enumerates several test smells most closely associated with test maintainability and uses code examples to demonstrate these smells and how to address these smells.

Chapter 5 focuses on the following "maintainability" test smells:

  • Duplication
    • Needless repetition that increases places where same change must be made and increases risk of not changing all necessary code
    • Duplication can be structural or semantic or both
  • Conditional Logic
    • "Conditional execution structures such as if, else, for, while, and switch" reduce the ability to use tests to "understand what the code does and what it should do"
  • Flaky Test
    • Tests that "fail intermittently," typically due to multithreading or race conditions
  • Crippling File Path
    • Absolute paths, especially hard-coded absolute paths, prevent unit tests from being run on others' machines
  • Persistent Temp Files
    • Files that are generated by unit tests may be less temporary than one realizes and interfere with later tests
  • Sleeping Snail
  • Pixel Perfection
    • Specialized version of Primitive Assertion and Magic Numbers test smells applied to exactly matching graphic representations in unit tests
  • Parameterized Mess
  • Lack of Cohesion in Methods
    • "Test methods in a test class are only interested in some of the fixture's objects"

As he did for the test smells covered in Chapter 5, Koskela provides code-based examples of each test smell discussed in Chapter 5 along with code-based examples of how to address each of the code smells.

Chapter 6: Trustworthiness

Chapter 6 finishes off Part 2 and the Catalog of Test Smells. Chapter 6's focus is on test smells closely associated with the degree of reliability and trustworthiness of tests. The test smells covered in this chapter are:

  • Commented-out Tests
    • Commenting out tests' implementations so they appear to pass when they really aren't run at all ("poor man's version control")
    • Removing @Test annotation from JUnit 4-based unit test method has same negative effect
    • Use of @Ignore (JUnit) is similar
  • Misleading Comments
    • Confuse what the tests are really testing (a famous code smell as well as test smell)
  • Never-failing Tests
    • "A test that can never fail is probably worse than not having that test ... Tests are supposed to fail when they should."
  • Shallow Promises
    • A "tests that does much less than what it says it does - or does nothing at all"
    • One type of "shallow promise" test smell (commenting out the body of the test method but not its signature and @Test annotation) misleads people to think test of something significant passed
    • Lack of assertions in a test method is another specific example of this test smell
    • Method name implying different functionality tested than what is actually tested is third example
  • Lowered Expectations
    • "Tests that are overly robust - they don't fail when they should," usually because "assertions are too vague"
  • Platform Prejudice
    • "Failure to treat all platforms equal"
  • Conditional Tests
    • "... conditional tests in our tests ... [are] bad in general"
    • "All branches in a test method should have a chance to fail"

Koskela uses code samples to illustrate the Chapter 6 code smells and what unit tests look like once the test smells are addressed with his recommendations. Chapter 6 wraps up Part 2 on the Catalog of Test Smells.

Chapter 7: Testable Design

Proponents of Test-Driven Development and even many proponents of any type of unit testing argue that unit testing is as much about design as it is about testing. As mentioned earlier, Koskela wrote about the Law of Two Plateaus in Chapter 1 and explained that significantly more benefit can be obtained from unit tests when they are used for more than quality assurance and are used in the design process. Chapter 7 returns to this idea and focuses on what it means to be a "testable design." Koskela defines testable design as "[making] it easy to instantiate classes, substitute implementations, simulate different scenarios, and invoke particular execution paths from our test code."

Koskela describes several design principles that lead to testable design: modular design, SOLID principles [Single Responsibility Principle, Open Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, Dependency Inversion Principle]. Koskela discusses these principles of object-oriented design and how software that adheres to them is inherently more testable./p>

Koskela articulates an advantage of writing unit tests early that I've definitely observed: "The act of writing tests before the implementation they call for is essentially a way to ensure that you're taking the client's view on the code you're shaping."

Section 7.2 of Effective Unit Testing looks at several "testability issues" and then Section 7.3 addresses these testability issues with "guidelines for testable design." It is worth nothing here that Section 7.2 and Section 7.3 (and really the entire book) make the assumption that these Java unit tests are run without use of reflection or bytecode manipulation. Using these tools, such as provided by PowerMock, would be one way to address some of these issues, but do add complexity to the unit tests.

I expected Section 7.3 to be the most controversial part of the book for me. It's not that I expected the book to be controversial; rather, it is the topic that is controversial. It has always bothered me when I have to change my production code design for no other reason than to accommodate a unit test. In practical terms, I often cave in and do this because the negative effect on my production code design is less significant than the benefit of unit testing, but I still don't have to like doing it.

In Section 7.3, Koskela describes some of the commonly-held assertions about testable Java code, including avoiding complex private methods, avoiding final methods, avoiding static methods, avoiding logic in constructors, avoiding the singleton, favoring composition over inheritance, and wrapping external libraries. I started reading this section expecting to think, "Yeah, but ...." Instead, I found myself largely agreeing with Koskela's arguments based on my own experience. His pragmatic attitude made his assertions in this section agreeable and realistic. For example, he advises to avoid "complex private methods" rather than simply stating avoid private methods altogether. I've always liked when good design principles are also good for testability and Koskela manages to couch most of these "testable design principles" as such.

Chapter 8: Writing Tests in Other JVM Languages

I'm a big fan of Groovy (127 blog posts to date labeled Groovy) and am increasingly interested in Scala, so I looked forward to reading Chapter 8 on unit testing with alternative JVM languages. Koskela provides a brief history of languages other than Java on the JVM and covers some benefits common to these alternative languages.

Although Koskela mentions Scala, Clojure, JRuby, Jython and even non-JVM Ruby in Chapter 8, the lion's share of the chapter is on testing with Groovy and Groovy-based tools. In particular, the chapter covers using Groovy for testing directly as well as using Groovy-based BDD testing frameworks easyb and Spock Framework.

Chapter 9: Speeding Up Test Execution

There are numerous factors that can demotivate Java developers when it comes to unit testing. Perhaps none is more significant than slowly executing tests. If running the tests starts to take too long, the developer might lose interest in running them as often. As they are used (run) less, the developer may start to question the investment of time and energy in writing and maintaining them. Also, as they are run less, it becomes increasingly likely that problems the test would flag will go on longer before being caught.

Koskela covers ideas for improving unit test execution performance in the ninth and final chapter of Effective Unit Testing. He begins by looking at why it is important to have fast tests (both builds and test execution) and follows that with looking at strategies to make the builds and execution of tests quicker.

Chapter 9 includes brief coverage of using Ant and Maven for building unit tests and how to profile build performance with both of those tools. Until reading this chapter I was not aware of Ant's built-in ProfileLogger (since 1.8) or Ant-Contrib. This chapter also demonstrates how to use Koskela's maven-build-utils extension. Ant's JUnitReport task and Maven's maven-surefire-plugin are also demonstrated in this chapter.

Besides covering build and execution profiling tools and how to use them to identify the tests that really need to have their performance addressed, Koskela also provides several tactical approaches one can use to improve test execution efficiency. These are solid ideas that I don't consider premature optimization because they are, for the most part, simply good ideas in general that potentially improve performance without sacrificing readability or maintainability of the tests. Another thing I like about these tactical approaches is that many of them have been covered in a slightly different perspective earlier in the book. This ending chapter now brings those previously discussed principles back into the discussion for improving test performance while at the same time addressing test smells.

All of Koskela's tactical suggestions for better performing unit tests make sense to me, but I particularly liked his coverage on database access in unit tests because I so often see this violated at extreme cost. I like his emphasized statement: "friends don't let friends use a database in their unit tests." He explains (and I agree) that integration tests are more appropriate for testing of actual database access.

There is a table in Chapter 9 (Table 9-1) that summarizes approaches one might take to address the two primary external constraints on unit test execution performance (CPU and I/O). Koskela then moves onto detailing how to implement some of these mitigation approaches. Several tools are covered in this chapter including some Linux tools, but two of the most interesting and new to me were Koskela's descriptions of how to use Amazon Web Services and GridGain to improve unit test building performance.

Appendices

Appendix A is a 7-page "JUnit Primer" that covers basics of JUnit with focus on assertions and use of Hamcrest matchers. Appendix B is a little over 6 pages on "Extending JUnit" with focus on runners and rules.

Test Smells

For me, the heart of Effective Unit Testing is Part 2 (plus Chapter 7 and Chapter 9 of Part 3 which I highlight next) that catalogs the test smells. This Part 2 is great at outlining issues I've run into with unit tests that can make unit testing more painful than it needs to be. It is also easy to see that there are trade-offs between the test smells such that the very approach that alleviates one test smell may increase risk of introducing another test smell. One example is the trade-off between duplication and readability in unit tests. The author talks about many of these trade-offs and provides rough guidelines of how to decide which way to lean without going too far in either direction. In the case of unit test readability at the cost of some duplication, I like stucampbell's take on this: "I'm more likely to refactor duplicated code for setting up state. But less likely to refactor the part of the test that actually exercises the code."

Testable Design and JUnit Performance

Like all three chapters in Part 2 on test smells, two of the chapters in Part 3 (Chapter 7 on testable design and Chapter 9 on unit test performance) are also chapters that I plan to re-read in the future. There is a lot packed into these chapters that directly address common unit testing issues as well as sparking ideas about other approaches that could be used to improve unit testing.

The Audience

As one would expect from a book with the title Effective Unit Testing, this book does indeed provide guidance on what separates effective unit tests from less effective unit tests. Not only does it cover practices one should use and should avoid, but it introduces terminology and cites well-known resources in the unit testing literature. As such, it is not only an appropriate book for someone with basic familiarity with unit testing who wants to improve their unit tests, but is also highly relevant to those new to unit testing in general and unit testing in Java in particular who want an overview of unit testing in Java. This book doesn't have enough details to be the only source of information for someone new to unit testing in Java, but does give the overall high-level view that can provide context for reading literature more specifically focused on frameworks and other unit testing tools.

Part 1 will be least interesting to those with significant experience with unit testing, but will be of high value to those who are new to unit testing or non-coding managers and leads who want an overview of why unit testing is important and what kinds of high-level things can make unit tests less effective or more effective. Although Part 1 was the least interesting to me, I still felt it was well-written and met the author's stated goal of providing a common context for the remainder of the book.

Part 2 and Part 3 are more technically detailed than Part 1. I like the overall descriptions of test smells and approaches to scrub these test smells in Part 2. I also really like the coverage in Part 3 of ideas for improving test build and execution performance and related to how to design code that is more testable.

What This Book is Not

As I have stated in this post and as the author reminds the reader of Effective Unit Testing, this book is not the book one should use to learn details of JUnit or other testing framework. There is just enough coverage of these to illustrate more general points, but the focus is on unit testing principles rather than on unit testing implementation libraries and frameworks. Although someone new to unit testing Java applications could find this book useful (particularly Part 1), some knowledge of JUnit would be beneficial (particularly for Part 2 and Part 3).

The Book Advantage

I have found that different mediums have different advantages when it comes to conveying information and learning. Although many of the principles found in this book are available online in various forms, the strength of the book is the author's organization and articulation of the ideas in a single coherent source. I think the book is well worth its price when I think of the time it would take to collect and organize these ideas from online sources. Furthermore, the author provides examples from his own and friends' experiences to add a "real life" feeling to it all. A well-written book's advantage over blog posts, forum threads, and the like is the ability to cohesively and coherently cover a topic with breadth and depth. This book does just that for the topic of unit testing in Java. To me this book may not offer a lot of new high-level concepts (although it does offer some new to me low-level details), but it articulates well the ideas and practices that seem to be emerging from collective experience writing unit tests for Java-based applications.

Breadth of References

Another thing I liked about Effective Unit Testing is the abundance of reference to sources with additional details on the ideas, concepts, tools, and frameworks referenced in the book. Some might argue that a downside of the book is that most of the concepts are not new. I actually argue the opposite: because these are practices for effective unit tests, one would expect them to be based on what more than one person has found to be effective through hard experience. When I am reading a book on "effective" anything, I'm not looking for something that is simply "new" or "different"; I am looking for things I should generally do and generally not do and why. By referencing others' work in unit testing as well as describing his own efforts in this area, Koskela increases the credibility of his book. I also liked the mixing of alternative languages, frameworks, and operating systems in the examples.

Breadth of Coverage

In 201 pages (not counting appendices, preface, etc.), Koskela articulates and demonstrates what has taken me years of experience to learn from writing and maintaining unit tests and from reading about unit testing. Even though the high-level concepts were not really new to me, I still learned several tactical approaches from this book. Besides learning some new tactics to employ to implement the concepts of unit testing in Java that I thought I already knew, this book has sparked additional ideas for improving my unit tests and has reinvigorated my interest in writing better unit tests.

Pragmatic Advice

I liked Koskela's pragmatism in this book. Some unit testing enthusiasts (or test-infected Java developers as he calls them in the book) can be overbearing in their enthusiasm and evangelism to the point where it's difficult to believe their claims. Koskela is obviously enthusiastic about unit testing, but seems to keep in mind one important truth: unit tests exist to benefit the quality of the design and code (the production code does not exist to benefit the unit tests). Koskela points out, for example, that there are cases where redundant unit test code might be easier to read and maintain than rigorously implementing DRY principles within test code at any cost.

Conclusion

I was a little apprehensive when I purchased Effective Unit Testing as part of Manning's MEAP, but I am glad that I did. This book delivered what I hoped for and I found Part 2 and Chapter 7 and Chapter 9 of Part 3 to be particularly useful for someone in my situation (relatively experienced Java developer looking for ideas to improve his or her unit testing). Although there wasn't a lot new to me at the highest level, there were a lot of interesting lower-level details that were new to me or were presented from a unique and interesting perspective. I also liked having these ideas I had from my own experiences laid out and articulated for me in printed form and I liked having these concepts being developed by the Java unit testing community all codified in a single book. The book packs a lot into 201 pages of regular text and the writing style is easy to read and understand. It is easy for me to recommend this book to Java developers who feel they have room to improve in writing of unit tests of Java applications. I also know many Java developers (including myself) who could benefit from reading this book.