Test-Driven Development with Java: Create higher-quality software by writing tests first with SOLID and hexagonal architecture

This document was uploaded by one of our users. The uploader already confirmed that they had the permission to publish it. If you are author/publisher or own the copyright of this documents, please report to us by using this DMCA report form.

Simply click on the Download Book button.

Yes, Book downloads on Ebookily are 100% Free.

Sometimes the book is free on Amazon As well, so go ahead and hit "Search on Amazon"

Drive development with automated tests and gain the confidence you need to write high-quality software

Key Features

  • Get up and running with common design patterns and TDD best practices
  • Learn to apply the rhythms of TDD – arrange, act, assert and red, green, refactor
  • Understand the challenges of implementing TDD in the Java ecosystem and build a plan

Book Description

Test-driven development enables developers to craft well-designed code and prevent defects. It’s a simple yet powerful tool that helps you focus on your code design, while automatically checking that your code works correctly. Mastering TDD will enable you to effectively utilize design patterns and become a proficient software architect.

The book begins by explaining the basics of good code and bad code, bursting common myths, and why Test-driven development is crucial. You’ll then gradually move toward building a sample application using TDD, where you’ll apply the two key rhythms -- red, green, refactor and arrange, act, assert. Next, you’ll learn how to bring external systems such as databases under control by using dependency inversion and test doubles. As you advance, you’ll delve into advanced design techniques such as SOLID patterns, refactoring, and hexagonal architecture. You’ll also balance your use of fast, repeatable unit tests against integration tests using the test pyramid as a guide. The concluding chapters will show you how to implement TDD in real-world use cases and scenarios and develop a modern REST microservice backed by a Postgres database in Java 17.

By the end of this book, you’ll be thinking differently about how you design code for simplicity and how correctness can be baked in as you go.

What you will learn

  • Discover how to write effective test cases in Java
  • Explore how TDD can be incorporated into crafting software
  • Find out how to write reusable and robust code in Java
  • Uncover common myths about TDD and understand its effectiveness
  • Understand the accurate rhythm of implementing TDD
  • Get to grips with the process of refactoring and see how it affects the TDD process

Who this book is for

This book is for expert Java developers and software architects crafting high-quality software in Java. Test-Driven Development with Java can be picked up by anyone with a strong working experience in Java who is planning to use Test-driven development for their upcoming projects.

Table of Contents

  1. Building the Case for TDD
  2. Using TDD to Create Good Code
  3. Dispelling Common Myths about TDD
  4. Building a typical application using TDD
  5. Writing our First Test
  6. Following the Rhythms of TDD
  7. Driving Design: TDD and SOLID
  8. Test Doubles: Stubs and Mocks
  9. Hexagonal Architecture: Decoupling external systems
  10. FIRST Tests and the Test Pyramid
  11. Exploring TDD with Quality Assurance
  12. Test First, Test Later, Test Never
  13. Driving the domain layer
  14. Driving the database layer
  15. Driving the Web Layer

Author(s): Alan Mellor
Edition: 1
Publisher: Packt Publishing
Year: 2023

Language: English
Pages: 348
City: Birmingham
Tags: Test-Driven Development; TDD; Java; SOLID Design Principle; Stubs; Mocks; Hexagonal Architecture

Cover
Title Page
Copyright and Credit
Dedicated
Contributors
Table of Contents
Preface
Part 1: How We Got to TDD
Chapter 1: Building the Case for TDD
Writing code badly
Understanding why bad code is written
Recognizing bad code
Bad variable names
Bad function, method, and class names
Error-prone constructs
Coupling and cohesion
Decreasing team performance
Diminishing business outcomes
Summary
Questions and answers
Further reading
Chapter 2: Using TDD to Create Good Code
Designing good quality code
Say what you mean, mean what you say
Take care of the details in private
Avoid accidental complexity
Revealing design flaws
Analyzing the benefits of writing tests before production code
Preventing logic flaws
Protecting against future defects
Documenting our code
Summary
Questions and answers
Further reading
Chapter 3: Dispelling Common Myths about TDD
Writing tests slows me down
Understanding the benefits of slowing down
Overcoming objections to tests slowing us down
Tests cannot prevent every bug
Understanding why people say tests cannot catch every bug
Overcoming objections to not catching every bug
How do you know the tests are right?
Understanding the concerns behind writing broken tests
Providing reassurance that we test our tests
TDD guarantees good code
Understanding problem-inflated expectations
Managing your expectations of TDD
Our code is too complex to test
Understanding the causes of untestable code
Reframing the relationship between good design and simple tests
Managing legacy code without tests
I don’t know what to test until I write the code
Understanding the difficulty of starting with testing
Overcoming the need to write production code first
Summary
Questions and answers
Further reading
Part 2: TDD Techniques
Chapter 4: Building an Application Using TDD
Technical requirements
Preparing our development environment
Installing the IntelliJ IDE
Setting up the Java project and libraries
Introducing the Wordz application
Describing the rules of Wordz
Exploring agile methods
Reading user stories – the building block of planning
Combining agile development with TDD
Summary
Questions and answers
Further reading
Chapter 5: Writing Our First Test
Technical requirements
Starting TDD: Arrange-Act-Assert
Defining the test structure
Working backward from outcomes
Increasing workflow efficiency
Defining a good test
Applying the FIRST principles
Using one assert per test
Deciding on the scope of a unit test
Catching common errors
Asserting exceptions
Only testing public methods
Preserving encapsulation
Learning from our tests
A messy Arrange step
A messy Act step
A messy Assert step
Limitations of unit tests
Code coverage – an often-meaningless metric
Writing the wrong tests
Beginning Wordz
Summary
Questions and answers
Chapter 6: Following the Rhythms of TDD
Technical requirements
Following the RGR cycle
Starting on red
Keep it simple – moving to green
Refactoring to clean code
Writing our next tests for Wordz
Summary
Questions and answers
Further reading
Chapter 7: Driving Design – TDD and SOLID
Technical requirements
Test guide – we drive the design
SRP – simple building blocks
Too many responsibilities make code harder to work with
Ability to reuse code
Simplified future maintenance
Counter-example – shapes code that violates SRP
Applying SRP to simplify future maintenance
Organizing tests to have a single responsibility
DIP – hiding irrelevant details
Applying DI to the shapes code
LSP – swappable objects
Reviewing LSP usage in the shapes code
OCP – extensible design
Adding a new type of shape
ISP – effective interfaces
Reviewing ISP usage in the shapes code
Summary
Questions and answers
Chapter 8: Test Doubles – Stubs and Mocks
Technical requirements
The problems collaborators present for testing
The challenges of testing unrepeatable behavior
The challenges of testing error handling
Understanding why these collaborations are challenging
The purpose of test doubles
Making the production version of the code
Using stubs for pre-canned results
When to use stub objects
Using mocks to verify interactions
Understanding when test doubles are appropriate
Avoiding the overuse of mock objects
Don’t mock code you don’t own
Don’t mock value objects
You can’t mock without dependency injection
Don’t test the mock
When to use mock objects
Working with Mockito – a popular mocking library
Getting started with Mockito
Writing a stub with Mockito
Writing a mock with Mockito
Blurring the distinction between stubs and mocks
Argument matchers – customizing behavior of test doubles
Driving error handling code with tests
Testing an error condition in Wordz
Summary
Questions and answers
Further reading
Chapter 9: Hexagonal Architecture –Decoupling External Systems
Technical requirements
Why external systems are difficult
Environmental problems bring trouble
Accidentally triggering real transactions from tests
What data should we expect?
Operating system calls and system time
Challenges with third-party services
Dependency inversion to the rescue
Generalizing this approach to the hexagonal architecture
Overview of the hexagonal architecture’s components
The golden rule – the domain never connects directly to adapters
Why the hexagon shape?
Abstracting out the external system
Deciding what our domain model needs
Writing the domain code
Deciding what should be in our domain model
Using libraries and frameworks in the domain model
Deciding on a programming approach
Substituting test doubles for external systems
Replacing the adapters with test doubles
Unit testing bigger units
Unit testing entire user stories
Wordz – abstracting the database
Designing the repository interface
Designing the database and random numbers adapters
Summary
Questions and answers
Further reading
Chapter 10: FIRST Tests and the Test Pyramid
Technical requirements
The test pyramid
Unit tests – FIRST tests
Integration tests
What should an integration test cover?
Testing database adapters
Testing web services
Consumer-driven contract testing
End-to-end and user acceptance tests
Acceptance testing tools
CI/CD pipelines and test environments
What is a CI/CD pipeline?
Why do we need continuous integration?
Why do we need continuous delivery?
Continuous delivery or continuous deployment?
Practical CI/CD pipelines
Test environments
Testing in production
Wordz – integration test for our database
Fetching a word from the database
Summary
Questions and answers
Further reading
Chapter 11: Exploring TDD with Quality Assurance
TDD – its place in the bigger quality picture
Understanding the limits of TDD
No more need for manual testing?
Manual exploratory – discovering the unexpected
Code review and ensemble programming
User interface and user experience testing
Testing the user interface
Evaluating the user experience
Security testing and operations monitoring
Incorporating manual elements into CI/CD workflows
Summary
Questions and answers
Further reading
Chapter 12: Test First, Test Later, Test Never
Adding tests first
Test-first is a design tool
Tests form executable specifications
Test-first provides meaningful code coverage metrics
Beware of making a code coverage metric a target
Beware of writing all tests upfront
Writing tests first helps with continuous delivery
We can always test it later, right?
Test-later is easier for a beginner to TDD
Test-later makes it harder to test every code path
Test-later makes it harder to influence the software design
Test-later may never happen
Tests? They’re for people who can’t write code!
What happens if we do not test during development?
Testing from the inside out
Testing from the outside in
Defining test boundaries with hexagonal architecture
Inside-out works well with the domain model
Outside-in works well with adapters
User stories can be tested across the domain model
Summary
Questions and answers
Further reading
Part 3: Real-World TDD
Chapter 13: Driving the Domain Layer
Technical requirements
Starting a new game
Test-driving starting a new game
Tracking the progress of the game
Triangulating word selection
Playing the game
Designing the scoring interface
Triangulating game progress tracking
Ending the game
Responding to a correct guess
Triangulating the game over due to too many incorrect guesses
Triangulating response to guess after game over
Reviewing our design
Summary
Questions and answers
Further reading
Chapter 14: Driving the Database Layer
Technical requirements
Installing the Postgres database
Creating a database integration test
Creating a database test with DBRider
Driving out the production code
Implementing the WordRepository adapter
Accessing the database
Implementing GameRepository
Summary
Questions and answers
Further reading
Chapter 15: Driving the Web Layer
Technical requirements
Starting a new game
Adding required libraries to the project
Writing the failing test
Creating our HTTP server
Adding routes to the HTTP server
Connecting to the domain layer
Refactoring the start game code
Handling errors when starting a game
Fixing the unexpectedly failing tests
Playing the game
Integrating the application
Using the application
Summary
Questions and answers
Further reading
Index
Other Books You May Enjoy