Why You Should Start Injecting Mocks as Method Arguments

One of the big improvements that came in JUnit 5 was support for dependency injection via constructors and methods. Since the release of JUnit 5 in September 2017, third-party libraries, like mockito, have started providing native support for constructor and method injection. In this article we will take a quick look at how to use constructor and method injection with mockito and then look at why you should start injecting mocks as method arguments in your automated tests.

How to Inject a Mock as an Argument

Starting with 2.21.0, current version 2.25.0, mockito has provided support for injecting mocks as both constructor and method arguments. Let’s looks at how you can start using dependency injection with mockito.

In your test class you will need to annotate it with @ExtendWith(MockitoExtension.class). Then for any arguments you would like mockito to provide a mock for, you simply annotate the argument with @Mock. Here is an example of using mockito dependency injection in action:

Pretty simple and straight forward. Let’s now look at why you should start using method injection of mocks.

The Case for Injecting Mocks as Method Arguments

There are three major benefits that come from automated testing: speed, repeatability, and auditability. The first two are pretty well understood benefits of automated testing, auditability however is if not less well understood, definitely less often discussed. Auditability, within the context of automated testing, refers to the quality of being able to see what code has been tested and the intent of the test.

Code coverage can be achieved without spending much time thinking about how other people, developers, test engineers, business analyst, etc, might use automated tests to understand (i.e. audit) the system the tests are covering. Tests with names like testSuccess, testFail, testFail2, can be executed just fine, but do little to communicate their intent. For an automated test suite to be properly auditable, tests names need to clearly convey the intent of what behavior is being tested. While a test with a name of testRollbackAddUserAddressValidationError​ is a bit of a mouth full, it pretty clearly describes what scenario the test is covering.

While testRollbackAddUserAddressValidationError()​ provides intent, to understand the scope of the test, what dependencies the code under test interacts with, it would require inspecting the code within the test case itself. However we can begin to communicate scope by injecting mocks as method arguments. If we were to do that with the above test we will would have testRollbackAddUserAddressValidationError(@Mock UserDao userDao). Now just from reading the signature of the test case we can determine that the scope of the test also includes interacting with the UserDao class.

When executing tests as a group, we can better see the benefits of injecting mocks as method arguments. Below is an example of running two test classes performing the same set of tests, but one is using mocks at the class level, while the other is using method injection. From the JUnit report alone, we can understand that UserService depends upon the UserDao and AddressDao classes.

Screen Shot 2019-03-12 at 11.20.15 AM.png

Note: Another new feature in JUnit 5 are nested tests, which is being used here.


Injecting mocks as method arguments isn’t game changing, but it can help make tests easier to read, and thus audit, thru being able to communicate in the signature the scope of the test. While there will be instance where passing a mock in as a method argument isn’t practical, generally that should be rare*, and so hopefully this article encourages you to generally use method injection when you are working with mocks.

The code used in this article can be found in this gist, and also this repo.

* Complex mock setup should be seen as a smell that either (or all) the mock, the test, or the code under test has too many responsibilities

Handling and Verifying Exceptions in JUnit 5

JUnit 5 offers a number of improvements over JUnit 4. In this article we will take a quick look at how exceptions are handled and verified in JUnit 4, and then see how the new assertThrows() in JUnit 5 improves the usability and readability when catching and verifying exceptions.

Handling and Verifying Exceptions in JUnit 4

In JUnit 4 there are two primary ways of handling exceptions. The most commonly used method is with the expected field in @Test. An alternative way of handling exceptions is by using a @Rule and ExpectedException. Below are examples of both: 

While both methods are capable of catching and verifying exceptions, each have issues that impact their usability and readability. Let’s step through some of these issues with expected and ExpectedException.

When using expected,  not only are you putting some of the assertion behavior into the definition of the test case, verifying fields within the thrown exception is a bit clunky. To verify the fields of an exception you’d have to add a try/catch within the test case, and within the catch block perform the additional assertions and then throw the caught exception.

When using ExpectedException you have to initially declare it with ​none(), no exception expected, which is a bit confusing. Within a test case you define the expected behavior before the method under test. This would be similar to if you were using a mock, but it’s not intuitive as a thrown exception is a “returned” value, not a dependency nor internal to the code under test.

These oddities significantly impacted the usability and readability of test cases in JUnit 4 that verified exception behavior. The latter is by no means a trivial problem as “easy to read” is probably one of, if not the, most import characteristics of test code. So it is not surprising then that exception handling behavior was heavily rewritten in JUnit 5.

Introducing assertThrows()

In JUnit 5, the above two methods of handling and verifying exceptions have been rolled into the much more straightforward and easier to use assertThrows(). assertThrows() requires two arguments, Class <T> and Executable, assertThrows() can also take an optional third argument of either String or Supplier<String> which can be used for providing a custom error message if the assertion fails. assertThrows() returns the thrown exception, which allows for further inspection and verification of the fields within the thrown exception.

Below is an example of assertThrows() in action:

As can be seen in the above, assertThrows()  is much cleaner and easier to use than either method in JUnit 4. Let’s take a bit closer look at assertThrows() and some of its  more subtle improvements as well.

The second argument, the Executable is where the requirement of Java 8 in JUnit 5 starts to show its benefits. Executable is a functional interface, which allows for, with the use of a lambda, directly executing the code under test within the declaration of assertThrows(). This makes it not only easier to check for if an exception thrown, but also allows assertThrows() to return the thrown exception so additional verification can be done.


assertThrows() offers significant improvements to usability and readability when verifying exception behavior for code under test. This is consistent with many of the changes made in JUnit 5, which have made the writing and reading of tests easier. If you haven’t yet made the switch to JUnit 5, I hope this seeing the improvements in exception handling and verification helps to build the case for making the switch.

The code used in this article can be found here: https://github.com/wkorando/junit-5-simple-demonstrator.

EDIT: An earlier version of this blog said that assertThrows()​ doesn’t support exception subtypes, that is incorrect.

What’s New in JUnit 5.4

It’s a new year and with that comes another release of the JUnit 5 framework! In this article we will look at some of the big new features released in JUnit 5.4.

Ordering Test Case Execution

I have been personally looking forward to this feature for sometime now. While unit tests by definition should be isolated from one another, JUnit covers a space larger than “just” unit testing. In my case, I have been wanting to be able to explicitly define test execution order to resolve an issue around an integration test scenario in a project demonstrating JUnit 5.

The goal of the integration test is to validate that the application can communicate with a Postgres database. In the test class, which is making use of TestContainers, three behaviors are being verified, reading, mapping, and writing to a database. For reading from the database, a simple count of the number of records is being used, which would obviously be impacted by writing a new record to the database. While tests in JUnit 5 are executed in a consistent order, it is “intentionally nonobvious” how that order is determined. With JUnit 5.4, we can finally define an explicit test execution order.

Let’s take a look at how to order test cases in a class (full class here):

To enable ordering tests cases in a class, the class must be annotated with the @TestMethodOrder extension and an ordering type of either AlphanumericOrderAnnotation, or Random must be provided.

  • Alphanumeric orders test execution based on the method name* of the test case.
  • OrderAnnotation allows for a custom defined execution order using @Order like shown above.
  • Random orders test cases pseudo-randomly, the random seed can be defined by setting the property junit.jupiter.execution.order.random.seed in your build file.
  • You can also create your own custom method orderer by implementing the interface org.junit.jupiter.api.MethodOrderer

*A test case’s @DisplayName, if defined, will not be used to determine ordering.

Order Only the Tests that Matter

When using OrderAnnotation you should note, and this can be seen in the code example above, you don’t have to define an execution order for every test case in a class. In the example above only one test has an explicit execution order, testCountNumberOfCustomersInDB, as that is the only test case that will be impacted by a change in state. By default JUnit will execute any tests without a defined execution order after all tests that do have a defined execution order. If you have multiple unordered tests, as is the case above, they will be executed in the default deterministic, but “nonobvious” execution order that JUnit 5 typically uses.

This design decision is not only helpful for the obvious reason of requiring less work, but it also helps prevent polluting tests with superfluous information. Adding an execution order to a test that does not need it, it could lead to confusion. If a test begins to fail, a developer or test automation specialist might spend time fiddling with execution order when the cause of the failure is unrelated to execution order. By leaving a test without a defined execution order it is stating this test is not impacted by state change. In short, it should be actively encouraged to omit @Order on test cases that do not require it.

Extension Ordering

The new ordering functionality isn’t limited to just ordering the execution of test cases. You can also order how programmatically registered extensions, i.e. extensions registered with @RegisterExentsion, are executed. This can be useful when a test(s) has complex setup/teardown behavior and that setup/teardown has separate domains. For example testing the behavior of how a cache and database are used.

While extensions by default execute in a consistent order, like test cases, that order is “intentionally nonobvious”. With @Order an explicit and consistent extension execution order can be defined. In the below example a simple extension is defined which prints out the value passed into its constructor:

Here is the console output from executing the above test class:

Executing beforeAll with value:C
Executing beforeAll with value:B
Executing beforeAll with value:A
Executing beforeEach with value:C
Executing beforeEach with value:B
Executing beforeEach with value:A
Executing afterEach with value:A
Executing afterEach with value:B
Executing afterEach with value:C
Executing beforeEach with value:C
Executing beforeEach with value:B
Executing beforeEach with value:A
Executing afterEach with value:A
Executing afterEach with value:B
Executing afterEach with value:C
Executing afterAll with value:A
Executing afterAll with value:B
Executing afterAll with value:C

Aggregate Artifact

A frequent question/concern I have heard when presenting on JUnit 5 has been the large number of dependencies that are required when using JUnit 5. With the 5.4 release the JUnit team will now start providing the junit-jupiteraggregate artifact. JUnit-Jupiter bundles junit-jupiter-api, junit-jupiter-params, so collectively this artifact should cover most of the needs when using JUnit 5. This change should help slim down the maven and gradle files of projects using JUnit 5, as well as make JUnit 5 easier to use in general. Below shows the “slimming” effect of the new aggregate artifact:


@TempDir began its life originally as part of the JUnit-Pioneer third-party library. With the release of 5.4, @TempDir has been added as a native feature of the JUnit framework. @TempDir makes the process of validating some file I/O behavior easier by handling the setup and teardown of a temporary directory within the lifecycle of a test class. @TempDir can be injected in two ways, as a method argument or as a class field and must be used with either a Path or File type. @TempDir cannot be injected as a constructor argument. Let’s take a look at @TempDir in action:

Note: The same directory is shared across a test class even if you inject a@TempDir in multiple locations.


TestKit was added in 5.4 as a way to perform meta-analysis on a test suite. TestKit can be used to check the number of; executed tests, passed tests, failed tests, skipped tests, as well as a few other behaviors. Let’s take a look at how you can check for tests being skipped when executing a test suite.

To use TestKit you will need to add the junit-platform-testkit dependency to your build file.

But That’s not All…

Another new feature added with 5.4 is the new Display Name Generator. Lee Turner already wrote a great article on the new display name generator, so rather than re-explaining it this article, check his: https://leeturner.me/blog/2019/02/building-a-camel-case-junit5-displaynamegenerator.html

This is only a highlight of some of the new features in JUnit 5.4, to view all the new features, changes, and bug fixes, checkout the release notes the JUnit team maintains: https://junit.org/junit5/docs/current/release-notes/

Also be sure to check out the JUnit 5 user guides for examples on how to use all the features in JUnit 5: https://junit.org/junit5/docs/current/user-guide/index.html


I have been continually impressed by the JUnit team’s steady work improving the JUnit 5 framework. In a little under a year and a half we have now seen four minor releases. As someone who has come to deeply appreciate and advocate for automated testing over the past couple of years, I am happy to see the JUnit team aggressively adding new features to JUnit 5 and taking in feedback from the community and other testing frameworks like Spock, TestNG, and others.

To view the code used in this article check out my project github page here: https://github.com/wkorando/WelcomeToJunit5

What’s New in JUnit 5.3

The JUnit team continues to make steady work refining and improving on JUnit 5. In this article we will take a look at some of the new features and changes that are in JUnit 5.3 which was released on September 3rd. Also in this article we will look at some recent changes and improvements to libraries and tools that are often used in conjunction with JUnit.

Parallel Test Execution

I covered parallel test execution in a previous blog article. If you want an technical and in-depth look at the parallel execution features in JUnit 5.3, give that article a read. Here is a high level overview of parallel test execution in JUnit 5.3:

  • Parallel test execution is enabled and configured through the build file (i.e. pom.xml, build.gradle)
  • Parallel test execution behavior can be modified at the method and class level with the annotations: @Execution and @ResourceLock.

Be sure to check out the user guide for more detailed information on configuring and using parallel test execution in JUnit 5.

AssertThrows Enhancements

Note: This feature has been reverted in 5.3.1

T̶h̶e̶ ̶a̶s̶s̶e̶r̶t̶T̶h̶r̶o̶w̶s̶ ̶a̶s̶s̶e̶r̶t̶i̶o̶n̶ ̶h̶a̶s̶ ̶b̶e̶e̶n̶ ̶u̶p̶d̶a̶t̶e̶d̶ ̶t̶o̶ ̶d̶i̶s̶p̶l̶a̶y̶ ̶t̶h̶e̶ ̶v̶a̶l̶u̶e̶ ̶r̶e̶t̶u̶r̶n̶e̶d̶ ̶f̶r̶o̶m̶ ̶a̶ ̶m̶e̶t̶h̶o̶d̶,̶ ̶i̶f̶ ̶a̶n̶y̶,̶ ̶w̶h̶e̶n̶ ̶a̶n̶ ̶e̶x̶c̶e̶p̶t̶i̶o̶n̶ ̶i̶s̶ ̶n̶o̶t̶ ̶t̶h̶r̶o̶w̶n̶.̶ ̶T̶h̶i̶s̶ ̶c̶a̶n̶ ̶b̶e̶ ̶h̶e̶l̶p̶f̶u̶l̶ ̶w̶h̶e̶n̶ ̶d̶e̶b̶u̶g̶g̶i̶n̶g̶ ̶a̶ ̶t̶e̶s̶t̶ ̶c̶a̶s̶e̶ ̶t̶h̶a̶t̶ ̶h̶a̶s̶ ̶s̶t̶a̶r̶t̶e̶d̶ ̶f̶a̶i̶l̶i̶n̶g̶.̶ ̶H̶e̶r̶e̶ ̶i̶s̶ ̶a̶ ̶s̶c̶r̶e̶e̶n̶s̶h̶o̶t̶ ̶o̶f̶ ̶w̶h̶a̶t̶ ̶t̶h̶i̶s̶ ̶d̶i̶s̶p̶l̶a̶y̶ ̶l̶o̶o̶k̶s̶ ̶l̶i̶k̶e̶ ̶i̶n̶ ̶E̶c̶l̶i̶p̶s̶e̶.̶ ̶I̶n̶ ̶t̶h̶i̶s̶ ̶e̶x̶a̶m̶p̶l̶e̶ ̶a̶n̶ ̶e̶m̶p̶t̶y̶ ̶L̶i̶s̶t̶ ̶i̶s̶ ̶b̶e̶i̶n̶g̶ ̶r̶e̶t̶u̶r̶n̶e̶d̶ ̶i̶n̶s̶t̶e̶a̶d̶ ̶o̶f̶ ̶t̶h̶e̶ ̶e̶x̶p̶e̶c̶t̶e̶d̶ ̶e̶x̶c̶e̶p̶t̶i̶o̶n̶.

Capturing Console Output

With JUnit 5.3 it is possible to capture System.out and System.err output using a TestExecutionListener. Capturing console output isn’t a frequent need, but might be necessary if working on more legacy code bases where statements are frequently being written to console and it would be nice to see what is being written in a report.

To start capturing console output it requires two steps:

1. Implementing a TestExecutionListener

2. Configuring surefire to start capturing console output.

Here is an example of each:


Surefire Configuration

Library and Tool Updates

As mentioned in the introduction of this article, we will be taking a look at updates to some other projects often used with JUnit. Most of these changes are unrelated too (i.e. not dependent upon) JUnit 5.3, but I wanted to cover them here as they happened relatively recently.

Surefire Native Support

Back in June the maven surefire team released version 2.22.0 of their plugin. With this release the surefire (and failsafe 2.22.0) plugin offer native support for JUnit 5. This is a nice, because prior to the 2.22.0 release, using JUnit 5 required a lot of additional configuration which was frankly a bit of an eyesore. Prior to 2.22.0, to run JUnit 5 with surefire this configuration was required:

With 2.22.0 the default surefire configuration can look like this:

Note: For spring boot projects, you need only add maven-surefire-plugin.version to your properties, like this.

Note 2: JUnit 5.3 has a bug that prevents tests from being found when the reuseForks tag is set to false. Read more here.

Filtering Changes with Native Support

However with native support comes changes with how tag filtering behaves. When using the JUnit surefire-provider in 2.21.0 filtering by tags looked like this:

However this does not work when running maven-surefire-plugin 2.22.0. To pass to surefire which tags you want to filter on you must use the groups, for which tests to execute, and excludeGroups, for which tests should not be executed, tags. Here is the 2.22.0 equivalent example of the above:

Enhanced Mockito Dependency Injection Support

As the tweet from @sam_brannen states, you can now directly inject mockito mocks via constructor or method parameters. This can make help make tests more declarative as to what they depend upon and/or a little cleaner. These changes were added in 2.21.0¹, here is an example of those changes in action:

AssertJ Soft Assertions and opentest4J

I have covered AssertJ in a previous blog post. With 3.11.1 AssertJ will now use the MultipleFailuresError in the opentest4j library, if it’s available. opentest4j is a library created by the JUnit 5 team to help make the testing experience more consistent across projects for both users and library developers.

If you are not familiar with soft assertions, they are a way of asserting multiple values in a single test and instead of the test failing on the first assertion, all the failed assertions are instead collected and reported at the end of the test. This can help avoid the irritating loop of a test failing, fixing the failure, running the test again, and hitting another failure. Here’s a look at soft assertions in AssertJ:

Running this test will return both assertion failures, instead of just reporting that the first assert failed.


The JUnit team continues to make great progress on JUnit 5 adding new features, improving on existing ones, and fixing bugs as they are reported. This is only a highlight of some of the new features and changes coming in JUnit 5.3. For a full list of all the changes and improvements coming besure to check out the release notes and the JUnit 5 user guide on how to use the features as well.

1: Technically the changes were added in 2.20.3, but that is only available from the mockito’s maven repository, not maven central.

Dynamic Testing in JUnit 5; a Practical Guide

JUnit 5 introduced several novel features, one of which is dynamic testing. I have been presenting on JUnit 5 fairly regularly, but have had trouble explaining dynamic testing. I demonstrated using a simple example, but would have a hard time providing a use case for dynamic testing. That was the case until somewhat recently when I saw this question on Stack Overflow.

When learning a new technology, I have always benefitted from following a guide that includes a practical example, as it helps me better visualize how I might use a technology to solve challenges I’m facing at my job. I’m sure I’m not the only person who learns this way, so in this article we will learn how to write dynamic tests, look at a practical example of a dynamic test, and learn about their role in our automated test suites.

Building a Factory for Tests

In JUnit 5 the first step when writing a dynamic test is annotating a method with @TestFactory. Unlike a method annotated with @Test, a method annotated with@TestFactory is a method for creating tests. This is conceptually different from the normal way of writing tests, which would be explicitly writing out the test within the method body, i.e. writing a test statically. This difference in mindset is why it can be difficult to figure out when to use dynamic tests.

To ease in to the process of learning dynamic tests, let’s first take a look at a really simple one:

We already covered @TestFactory above, lets take a look at the other elements of a dynamic test.

Dynamic tests must return either a CollectionIterableIterator, or Stream, of DynamicNode types. DynamicNode is an abstract class with two default implementations of DynamicTest and DynamicContainer. We will cover DynamicContainer later, DynamicTest however is the executable test composed in a @TestFactory method that will be executed by JUnit.

For building DynamicTests the JUnit team has provided some factory methods, in the above example I am using dynamicTest(String displayName, Executable executable)displayName is the name that will show up in test reports¹ and executable is a method reference or lambda of the code that will actually be executed in the test.

Writing dynamic tests as can be seen above is pretty simple. However the above example doesn’t provide much guidance on how dynamic tests might be used to ensure our applications are being properly covered by automated tests. Let’s take a look at a more practical application of a dynamic test.

Writing Dynamic Tests to Reduce Toil

It’s not uncommon the need to write a service that communicates with multiple clients, and those clients communicating in different data formats. To demonstrate the power and utility of dynamic tests we are going to walk through writing a dynamic test that scans an application for controllers, check if those controllers have a POST endpoint, and then verify the POST endpoint can consume both XML and JSON.

Writing out tests to ensure every POST (and PUT) endpoint in a RESTful API accepts all required content types can be tedious and error prone. It is easy to imagine a scenario where a developer doesn’t properly configure an endpoint and then either not write the tests that validate the endpoint for content types it accepts or have a copy/paste error (doesn’t update the test to call the correct endpoint). There is a good possibility this bug won’t be caught until it reaches production. Let’s look at how a dynamic test can make this process easier, checking all POST endpoints to verify they accept required content types, and as new POST endpoints are added, automatically checking them as well.

Below is a dynamic test that accomplishes this, lets walk through what this code is doing:

The dynamic test can be broken down into roughly three steps:

  1. Scan the classpath for controller classes
  2. Mock out controller dependencies
  3. Build the tests to call the POST endpoints

To handle the first steps we will use of Spring’s ClassPathBeanDefinitionScanner. The classpath scanner has been configured to look for any class annotated with @RestController and to scan from the root package of the project. This should find any controllers within the project, ClassPathBeanDefinitionScanner can be easily configured though to narrow or widen its search criteria as might be needed in your project.

Once all the controllers in the project are found, we must figure out any dependencies the controllers require and create mocks, in this case dummies, of those dependencies. While this task could be accomplished if field injection was being used, it’s made easier by using constructor injection. Using constructor injection instead of field injection helps to define the required elements a class must have in order to be in a usable state. It’s for this reason Pivotal engineers and developer advocates recommend using constructor injection.

For the third step, we need to create the tests themselves. The first part of this step is building the URL that MockMvc will call. The code is checking to see if a @RequestMapping has been defined at the class level providing the base of the URL for the controller. Next we will check if any methods in the controller have been annotated with @PostMapping. We are assuming proper RESTful practices, so the POST endpoint should reside at the base of the controller URL.

Finally we are using mockMvc to execute the POST call and what we will be checking for is that a 415, media type not accepted, is not being returned. By checking that a 415 is not being returned, we make the process of writing the dynamic test easier as we are able to use dummies for our controller dependencies. If we were to check for a 200 (OK), this would not only add a lot of overhead, as the mock dependencies would need to have behavior, but also distract from the purpose of the test, we are checking that POST endpoints are accepting required content types, not testing any of the code within the endpoints themselves. Applying the single responsibility principleto tests as well, makes writing tests easier and automated test suites cleaner.

Executing the above code will return four hopefully passing tests that look like this:

This isn’t too bad of test reporting, but as a RESTful API increases in complexity it might become a little difficult to read. Let’s look at how DyanmicContainers can be used to make the test report a little easier to read and scale better as new controllers and POST endpoints are added.

I cut out some of the code above for brevity, but what has been changed is in the method signature a Collection of DynamicContainers is being returned instead of DynamicTests. Next we have a variable dynamicContainers that is also a List of DynamicContainers. Within the for loops where the tests are being created there is now a second ListdynamicTests, and we are adding the tests we are building to that list. Finally we are adding dynamicTests to dynamicContainers.

With those changes made, when we run the test again the report we get should look something like this:

Now the test for each content type is nested under the the base URL of the controller. This will help keep the report readable as new controllers and POST endpoints are added.

When to Use Dynamic Testing

The benefit dynamic testing offers over statically writing tests doesn’t come in the form of time saved, but from avoiding toil and saving mental capacity. In the example above of verifying that POST endpoints can consume JSON and XML, it would be laborious writing out a set of tests verifying as such each time a new POST endpoint is added. This task would often be accomplished instead by copying and pasting the tests that were used to verify another endpoint. As mentioned earlier it’s pretty easy to imagine then a developer forgetting to update those tests to call the correct endpoint.

This leads to what I think is the biggest tell (or smell if you like) for when to use dynamic tests, copying and pasting tests from other test classes. Other common testing needs for example security and database communication often have a series of standard test; if this role is present allow, if not deny, if all the required fields are present update the table, if not throw an error. Developers often save time by copying, pasting, and updating tests, but it would be better to write out a single dynamic test that will then automatically generate new tests verifying common behavior as new endpoints, tables, security roles, etc. are added.


It’s exciting to see so many new possibilities that come from testing with JUnit 5. In an earlier article I covered how test interfaces introduced in JUnit 5 could be used to make testing easier while also encouraging developers to follow architectural patterns. As the JUnit team continues adding new features to JUnit 5 I’m sure we will find new an exciting ways to write tests that weren’t possible before.

The code in this article can be found here:

Simple dynamic test: https://github.com/wkorando/junit-5-simple-demonstrator

Practical dynamic test: https://github.com/wkorando/WelcomeToJunit5

  1. There is currently a bug in surefire where the display name of dynamic tests is not being used.