The course is part of this learning path
Test-Driven Development (TDD) is a discipline that helps to create error-free, quality code, and drives productivity by reducing and eliminating errors as well as providing many other benefits.
This entry-level training course is designed to bring you quickly up to speed with the basic features and processes involved in performing Java based test-driven development using JUnit5. It covers the key principles and practices of test-driven development, which you can then utilize within your own software engineering projects.
In this course, you'll be guided through an end-to-end sample project in which we use test-driven development to build, test, and package a Java 11 based library that will provide an API to convert Bitcoins into foreign currency.
If you have any feedback relating to this course, please contact us at email@example.com.
- Understand the fundamentals of TDD and JUnit5
- Use TDD to build, test, and package a Java 11 based library
- Perform TDD by performing Red-Green-Refactor iterations
- Learn how to use TDD to build, test, and package a Java 11 based library
- Learn how to create unit tests using the JUnit5 testing framework
- Software developers interested in using TDD to build JDK11 based applications using Java
To get the most out of this course, you should have a basic understanding of JDK11 and the Java programming language, as well as software development and the software development life cycle (SDLC).
Welcome back. In this lesson, I'm going to provide you with a quick review of unit testing, what it is, and how you go about performing it yourself. I'll reveal the basic structure of a unit test, as well as highlighting best practices for naming and building your own.
Let's begin. Up until now, I've largely referred to test-driven development tests, being just that, tests. But what are they really, in a real-world, software engineering project. Most often, test-driven development tests, are implemented as unit tests. A unit test is exactly that, a test that is focused on testing a discrete or smaller part of a larger system and done so in an isolated manner.
A unit test in the context of this course tests the runtime behaviors of a class in isolation. A Java class is, as we all know, nothing more than a template for constructing objects at runtime. A class typically contains methods and properties, which implement and manage both the state and behavior, within an object instantiation of that class. Therefore, unit tests that target and test Java classes, need to test and validate each of the individual methods, implemented within the class file.
Unit tests should focus on testing the behaviors of a class and nothing more. By this very definition, they should execute fast, mostly in a matter of seconds and entirely in memory. This in fact, is the defining property of a unit test. It's not uncommon to execute hundreds of unit tests in a matter of seconds.
If a unit test instead takes a very long time to run, perhaps minutes to execute, then it's probably not a unit test but, instead, a misplaced integration test. Meaning this test should be moved elsewhere. Examples of this include, a test involving network communication, a test reading or writing to a file system, a test transacting with a database, or a test involving console input or interaction from a user.
Again, to reiterate, unit tests are meant to execute fast. If they don't, then they probably aren't unit tests. When it comes to performing Java-based unit testing, there are many unit testing frameworks available to choose from, but rather than calling all of them out, I'll quickly review the most popular one, that being JUnit. JUnit is an open-source, unit testing framework, and currently is available in two main versions, JUnit 4 and JUnit 5. The newer version, JUnit 5, adopts the Java-like style of coding and is considered more robust and flexible, than its predecessor.
In this course, I've chosen to go with JUnit 5. JUnit 5 provides an intuitive interface for crafting a unit test. It's quick and easy to install and just as easy to begin creating unit tests. In terms of learning JUnit 5, the good news is, as already mentioned, it's highly intuitive and therefore, very easy to learn as you'll witness, when observing the given demonstrations.
Let's now take a quick look at what a unit test, actually looks like. In the given example displayed here, we have created a single unit test that tests a static method, declared within the calculator class. The unit test itself has been developed, using the JUnit 5, unit testing framework, which I'll go into deeper in the next lesson.
Next, I want to highlight that unit tests, are most often structured, using the Arrange-Act-Assert pattern, sometimes referred to as the triple A pattern. The parts of this pattern include the arrange part. This is used to set up the code under test. Here, objects being tested get instantiated. Marks, if used, are set up and expectations are set. The act part is used to execute the method and behavior, under test. In the assert part, here, assertions are defined to check whether specified expectations were met or not. Following the Arrange-Act-Assert pattern, will help other members of your development team to quickly understand the intention of your unit test.
Moving along, when it comes to naming standards for your unit test, there's no hard and fast rule, which is enforced at compile or runtime. However, do consider carefully, the way you name your unit tests, particularly the method names. Your test method names can help others who are unfamiliar, with your unit test, understand the specific unit under test and expected behaviors and outcomes for particular states.
Now, before I promote an actual test naming strategy, let's first review motives for such a strategy. One, test names should make reference to the method under test. Two, test names should express the overall intention of the method under test. Three, test names should make reference to both the inputs or state and their resultant output or expectation. And four, test names, collectively should describe, the overall behavior of the unit or class under test.
Now that I've covered off the motives for a successful unit test naming strategy, let's formalize it by stating the naming strategy, which I'll use, and the later given test-driven development, coding demonstrations. For the demonstrations provided within this course, the particular naming strategy I'll use, will be the following.
This naming strategy breaks down from left to right. The name of the method being tested, underscore, the state being introduced or tested within the method underscore, and then the expected resulting behavior. With this naming strategy, it should be very clear to someone new to the project, exactly what is being tested.
Examples following this particular naming strategy for our class, let's say, for example, influence, a square root function would be square root, underscore, positive number, underscore, returns valid square root. Square root, underscore, zero, underscore, returns zero. And square root, underscore, negative number, underscore, throws exception. This unit test naming strategy makes it very clear, as to what is being tested. The state the test is acting on, and the expected outcomes.
Okay, that now completes this lesson on unit test. In this lesson, you learned about what a unit test is. You learned that a unit test is designed to test the runtime behaviors of a class and nothing else. You learned that unit tests should run very fast, in a matter of seconds. You learned that unit tests are best structured, using the Arrange-Act-Assert pattern. And, you learned that naming your unit test appropriately, will help others understand quickly and easily, the intention of your unit test.
Go ahead now and close this lesson and I'll see you shortly in the next one.
Jeremy is a Content Lead Architect and DevOps SME here at Cloud Academy where he specializes in developing DevOps technical training documentation.
He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 25+ years. In recent times, Jeremy has been focused on DevOps, Cloud (AWS, GCP, Azure), Security, Kubernetes, and Machine Learning.
Jeremy holds professional certifications for AWS, GCP, Terraform, Kubernetes (CKA, CKAD, CKS).