Icon for gfsd IntelliJ IDEA

A beginner's guide to unit testing 2/4: How to use unit tests?

Find out how to use unit tests in part 2 of our guide to unit testing

Part 2 of our introduction to unit testing covers how to write unit tests and presents some unit testing tools for Java.

Unit tests form a core practice of software development, but not all developers are adept at using them. Symflower’s 4-part series covers all aspects of unit testing to provide you with the information you need to get started with unit testing your code.

Check out all the other parts of this series:

When should you use unit testing?

Because of its benefits mentioned in part 1, engaging in unit testing is a good practice for anyone writing code. Some argue that unit tests are most valuable in the case of software applications in the growth phase and until the very end of their maturity phases. We at Symflower strongly believe that unit testing helps in all stages of a product’s maturity.

Unit tests are generally used during the development phase of the lifecycle. In a Test-Driven Development environment, writing unit tests is actually the very first step of development. If you’re building applications in a traditional (as opposed to TDD or BDD) environment, you’ll write unit tests right after delivering a portion of code (e.g. an individual class, method, function, or a line of code).

But they shouldn’t just be an afterthought – rather, unit tests should form a core part of your development workflow. Immediately after delivering a section of code, you’ll go on to testing it with unit tests. Private methods are usually not tested.

Some developers choose trivial code to leave untested, such as getters and setters without any logic. It’s also common for teams to configure their automated build systems so that unit (and integration) tests are run as part of the build process.

Unit tests make it easier to detect bugs in a small amount of code vs checking a large bit of code for robustness, so you’ll want to test early and often. (You may recognize those principles from the shift-left movement).

👀 Curious about TDD?

Check out our guide for getting started with Test-Driven Development!

Download Symflower's white paper: An introduction to Test-Driven Development
Click the image to download the white paper

How to write unit tests?

Start by making sure you have a thorough understanding of the functionality you’re aiming to test. Set up your test environment (import libraries & modules) and use a unit testing framework like JUnit.

🤔 Need help picking a unit testing framework?

Read our comparison of JUnit vs TestNG!

It’s a best practice to write test cases using the Arrange-Act-Assert pattern:

  • Arrange: Define inputs and expected outputs for the test to check
  • Act: Call the function or method with the setup data
  • Assert: Compare the output to the expected result

Don’t forget to tear down (e.g. “clean up after”) the test. You’ll also want to make sure that your test suite covers edge cases and boundary cases.

Unit tests should be simple, fast to run, easily readable, and logically organized. Avoid grouping assertions and focus on a single business functionality.

Isolate your tests. Make use of test doubles like mocks, stubs, fakes, or spies.

Your tests should return deterministic results. Don’t make your tests dependent on other tests, external services, or the environment they are run in.

Try Symflower in your IDE to generate unit test templates & test suites

Here’s a quick example of how that should be done in practice. First, here’s the implementation we’re going to be writing a unit test for:

class Copy {
    static String[] copy(String[] from, String[] to) {
        
        for (int i = 0; i < from.length; i++) {
            to[i] = from[i];
        }

        return to;
    }
}

Next up, here’s a unit test that checks if our copy function overwrites null values:

public class CopyTest {
   @Test
   public void copyOverNullValues() {
      String[] from = { "abc", "abc", "abc" };
      String[] to = { null, null, null };
      String[] expected = { "abc", "abc", "abc" };


      String[] actual = Copy.copy(from, to);


      assertArrayEquals(expected, actual);
   }
}

Unit testing tools (for Java)

Making use of the most important unit testing tools will help make unit testing an efficient and effective part of your Java coding workflow.

Unit testing framework: JUnit

Let’s start with the basics: for most Java developers, JUnit is the unit testing framework of choice. It is open-source and helps create, run, and report on automated unit tests.

JUnit also works well with Maven and Gradle. It supports parameterized testing, provides simple ways to ignore or time out tests and to specify the sequence in which your tests are run. JUnit also helps reporting by providing XML reports of your test runs.

🕵️ What’s the best unit testing framework around?

Check out our post about the best Java unit testing frameworks & tools

Mocking framework: Mockito

Mockito is the mocking framework of choice for tons of Java developers. It makes it easy to use fakes, stubs, mocks, and spies to remove external dependencies for testing purposes.

You can use annotations and exceptions in your mocks and Mockito lets you check the order of method calls. Mockito test code will continue to work even if a method is renamed or its parameters are reordered, supporting easy refactoring.

👯‍♀️ Let’s compare mocking frameworks

Check out our guide to mocking frameworks for Java which compares Mockito vs EasyMock vs JMockit!

Test coverage tool: JaCoCo

JaCoCo provides a versatile solution for analyzing test coverage. In addition to providing general code coverage data, it lets you analyze test coverage for instructions, branches, lines, methods, and types. JaCoCo will give you a visual report about test coverage that you can export as HTML, CSV, or XML.

Test generation tool: Symflower

Symflower is a smart IDE plugin that generates JUnit test templates without any manual effort required. These smart test templates come with all the imports, annotations, object initializations, function calls, asserts, etc that you’ll need. All you need to do is fill in test values for your test scenarios.

A beta feature also lets you generate complete unit test suites with meaningful values that explore all paths of your application. The tool also handles mocks (using Mockito).

Summary

That should help you get started with unit testing and build it into your everyday workflow. Was that a little too advanced? Take a step back and look at part 1 where we cover the basics of unit testing.

Hungry for more? Check out part 3 on automating unit tests and part 4 for some best practices to improve your unit testing!

Make sure you never miss any of our upcoming content by signing up for our newsletter and by following us on Twitter, LinkedIn or Facebook!

| 2024-03-13