---
title: "It's Time to Migrate: JUnit 5 is Better than JUnit 4"
description: Ready to enhance your tests and make them more readable? JUnit 5 is the robust and flexible framework you didn't know you needed.
image: https://blog.avenuecode.com/hubfs/E002F4B3-17E2-450F-9A73-D4525B33FD91_1_201_a.jpeg
---

[AvenueCode.com](https://avenuecode.com) [News](https://avenuecode.com/news) [Contact](https://avenuecode.com/contact)

[![Avenue Code Snippets Logo](https://blog.avenuecode.com/hubfs/Avenue%20Code%20New%20Logos%20-%202023/AC-Snippets---Black.png)](https://blog.avenuecode.com/?hsLang=en-us) *menu*

- Technology
  
  [Cloud](https://blog.avenuecode.com/blog/topic/cloud?hsLang=en-us) [Delivery Infrastructure](https://blog.avenuecode.com/blog/topic/delivery-infrastructure?hsLang=en-us) [Web Experience](https://blog.avenuecode.com/blog/topic/web-experience?hsLang=en-us) [Agile Mindset](https://blog.avenuecode.com/blog/topic/agile-mindset?hsLang=en-us) [Quality First](https://blog.avenuecode.com/blog/topic/quality-first?hsLang=en-us)
  
  [Design](https://blog.avenuecode.com/blog/topic/design?hsLang=en-us) [Solution Architecture](https://blog.avenuecode.com/blog/topic/solution-architecture?hsLang=en-us) [Data & ML](https://blog.avenuecode.com/blog/topic/data-and-machine-learning?hsLang=en-us) [Mobile Experience](https://blog.avenuecode.com/blog/topic/mobile-experience?hsLang=en-us)
- [Whitepapers](https://blog.avenuecode.com/blog/topic/whitepapers?hsLang=en-us)
- [Spotlight](https://blog.avenuecode.com/blog/topic/spotlight?hsLang=en-us)
- [Extraordinary Women in Tech](https://blog.avenuecode.com/blog/topic/extraordinary-women-in-tech?hsLang=en-us)
- [Avenue Code Culture](https://blog.avenuecode.com/blog/topic/avenue-code-culture?hsLang=en-us)

- [Cloud](https://blog.avenuecode.com/blog/topic/cloud?hsLang=en-us)
- [Design](https://blog.avenuecode.com/blog/topic/design?hsLang=en-us)
- [Delivery Infrastructure](https://blog.avenuecode.com/blog/topic/delivery-infrastructure?hsLang=en-us)
- [Solution Architecture](https://blog.avenuecode.com/blog/topic/solution-architecture?hsLang=en-us)
- [Web Experience](https://blog.avenuecode.com/blog/topic/web-experience?hsLang=en-us)
- [Data & ML](https://blog.avenuecode.com/blog/topic/data-and-machine-learning?hsLang=en-us)
- [Agile Mindset](https://blog.avenuecode.com/blog/topic/agile-mindset?hsLang=en-us)
- [Mobile Experience](https://blog.avenuecode.com/blog/topic/mobile-experience?hsLang=en-us)
- [Quality First](https://blog.avenuecode.com/blog/topic/quality-first?hsLang=en-us)
- [Whitepapers](https://blog.avenuecode.com/blog/topic/whitepapers?hsLang=en-us)
- [Spotlight](https://blog.avenuecode.com/blog/topic/spotlight?hsLang=en-us)
- [Extraordinary Women in Tech](https://blog.avenuecode.com/blog/topic/extraordinary-women-in-tech?hsLang=en-us)
- [Avenue Code Culture](https://blog.avenuecode.com/blog/topic/avenue-code-culture?hsLang=en-us)

# [It's Time to Migrate: JUnit 5 is Better than JUnit 4](https://blog.avenuecode.com/its-time-to-migrate-junit5-is-better-than-junit4)

- [Tweet](https://twitter.com/share)

*perm\_identity* [Lucas Moraes](https://blog.avenuecode.com/its-time-to-migrate-junit5-is-better-than-junit4/author/lucas-moraes?hsLang=en-us)

*schedule* 5/5/21 2:00 PM

Are you still using JUnit 4? If so, your code will thank you for migrating to JUnit 5.

JUnit 5 is the latest stable release of JUnit, and it comes with a lot of great features. Some of them are there to make your tests more readable. Some were added so you can enhance your written tests and add new scenarios. JUnit 5 also allows you to get the most out of Java 8 language features, such as lambda expressions. In general, JUnit 5 is trying to make the framework more robust and flexible than it was in its previous version.

![](https://lh3.googleusercontent.com/RRVvrZCbIU5h5uE5GkGCHJvuMVY7O-_-gXvvC7iBXU8f4Zl3tqkmNPCg63KkgFvxaQ7l5LxlU6YDS7tTp9d8Q2kG5jjxd1DzqhRmUGf4MJcL7ZXlUMblEv8r-E3aSZtxc2Y2bz2B)

##### Annotation Changes

The first changes that we will be discussing are annotation changes. JUnit 5 tried to increase readability for all annotations so that they are easier to understand at first glance. The new framework also has some new annotations that we can use to increase our scenarios. Let's take a look at the major annotation changes first:

@Before → @BeforeEach  
@BeforeClass → @BeforeAll  
@After → @AfterEach  
@AfterClass → @AfterAll

First of all, let's talk about the simplest change. Before was renamed to BeforeEach, BeforeClass was renamed to BeforeAll, After was renamed to AfterEach, and AfterClass was renamed to AfterAll. All of these changes were made to enable cleaner code and better readability.

@RunWith → @ExtendWith

RunWith was renamed to ExtendWith. But this one wasn't just a simple rename. ExtendWith is an annotation with a lot of features. The best one is that you can now use multiple Extensions at the same time. For example, you can do something like this:

**@ExtendWith(MockitoExtension.class)@ExtendWith(MyCustomExtension.class)public class FeatureTest {}**

Alternatively, you can just use a list inside the @ExtendWith value, as shown below:

**@ExtendWith({MockitoExtension.class, MyCustomExtension.class})public class FeatureTest {}**

Note that the execution order will follow the execution declared.

@Suite → @SelectPackages or @SelectClasses

Another change was made to improve the readability and to give each annotation single responsibility. So if you want to do a test suite, it's easier with the above annotations. Let's see how this can be done:

**@SelectClasses({PostgreConnector.class, PostgreRepository.class, PostgreDriver.class})public class MyPostgreTests {}**

In the test above, we pointed to each class that we wanted to cover in our test suite. We can do this by using packages, as shown below:

**@SelectPackages("com.avenuecode.examples.junit5.database")@IncludePackages("com.avenuecode.examples.junit5.database.pgsql")public class MyPostgreTests {}**

In the test above, I'm using a new annotation too: **@IncludePackages.** **@SelectPackages** will make sure that all classes and subpackages under that package will be tested. If you also annotate with **@IncludePackages**, you will make sure that only that subpackage and its classes will be in that suite. You can also use **@ExcludePackages** to point a package and its subpackages to be excluded from a test suite. In addition, you can use **@IncludeClassNamePatterns** or **@ExcludeClassNamePatterns** to make sure that some classes following a regex indicated will be excluded or included in your suite.

@Ignore → @Disabled

This was a rename that also aims for better readability and an easier understanding of what the method does. Beyond this, if you annotate **@Disabled** at class level, you will get the number of tests skipped in your execution log. If you used **@Ignore** with JUnit 4, you would get: **1/1 class skipped**, but now you will get: **number of tests/number of tests skipped,** which makes a slight improvement in understanding what was skipped.

# ![](https://lh5.googleusercontent.com/d0xhgRkZjgILesrrenU80xwSOPH9VYHTD79vJaXk9WVntDVP1Z9lSHXr74HD9cENA4C5nCUzT9Ul-38oWRNNANS9msMMu2U2UIL2MEK5JQ4CopEGy2nB2bYcMDG8gN2r0wFkWBRt)

##### New Annotations

As stated before, JUnit 5 added some new annotations that can help us increase our test readability and make them more powerful. We will be focusing on two annotations in this article to showcase an example of what the new version is trying to achieve: **@DisplayName** and **@ParameterizedTest**.

@DisplayName

This annotation is one of those changes that you look at and think: "Why wasn't this implemented before?". It's so simple and yet so functional. All this change does is display a text before your test runs. It's that simple. Here's the code:

**@Test@DisplayName("The method withdraw should reduce the amount of money in the bank account by the amount passed")public void withdrawShouldReduceAmount() {    var bankAccount = new BankAccount(2000);    bankAccount.withdraw(100);    assertEquals(1900, bankAccount.getBalance());}**

@ParameterizedTest

This annotation is one of the most powerful ones. Why? It allows you to reutilize the same test method with multiple parameter variations. Yes, this was a feature only achieved by adding external libraries; now it's on JUnit source too. Below, I'll show how to write a simple test that can use this feature very well. 

Let's say we want to check whether or not our customer who has a bank account is eligible for a credit card. Our method would look something like this:

**public enum AccountType {    BLACK(1000), PLATINUM(1500), GOLD(2000);    private double minEligibility;    AccountType(int minEligibility) {        this.minEligibility = minEligibility;    }    public double getMinEligibility() {        return this.minEligibility;    }}public boolean isEligibleForCreditCard() {   return this.balance >= this.accountType.getMinEligibility();}**

So now, we can write our tests like this:

**@ParameterizedTest@MethodSource("accountTypes")@DisplayName("Verify if the account is eligible for a credit card based on its type and balance")public void verifyCreditCardEligibility(BankAccount bankAccount, boolean expected) {    assertEquals(expected, bankAccount.isEligibleForCreditCard());}**

**public static Stream<Arguments> accountTypes() {    return Stream.of(            Arguments.of(new BankAccount(AccountType.GOLD, 3000.5), true),            Arguments.of(new BankAccount(AccountType.GOLD, 1000.25), false),            Arguments.of(new BankAccount(AccountType.BLACK, 1100), true),            Arguments.of(new BankAccount(AccountType.BLACK, 980), false),            Arguments.of(new BankAccount(AccountType.PLATINUM, 1600), true),            Arguments.of(new BankAccount(AccountType.PLATINUM, 1200), false)    );}**

What we're doing above is providing a data source method. ParameterizedTest will use our **MethodSource** AccountTypes to supply the test parameters. So, for each of our **Arguments** of the **Stream** we're creating in our **MethodSource**, the test method will be run passing the **Arguments** as parameters to **verifyCreditCardEligibility()**. Pretty simple, yet powerful, right?

You can provide other sources of data, such as **@ValueSorce, @EmptySource, @EnumSource**, and a lot of others. You can find a full guide [here](https://www.baeldung.com/parameterized-tests-junit-5).

# ![](https://lh3.googleusercontent.com/EEDwEgOCfhuYLTrKNHdsI5x1Pbwog2hynes_eKmn5rqnmpKsLzSVIvqPDowVzW_x5VSEbE2gzojY2eusCziv8xZmQoA4Y4VGY4hbg7ibkbb0d-2m_Q83PPqvhMOC6gsawLCQElz7)

##### New Assertions and Changes

The new assertions are located in a new package. Most of them are located in org.junit.jupiter.Assertions.

Like the changes shown above, some assertions were changed to achieve better readability. New assertions were added so we can achieve new scenarios or make some old cases more powerful. Let's take a loot at each:

Message is now the last parameter of assert\[...\]()

Did you also hate it when you wrote some assertion like **assertEquals("Some string", "Another string")**, and then you wanted it to have a custom message like "*2 is not the same as 2"* when it failed, but to achieve that you needed to change your assertEquals parameter order? That was all because the overloaded methods were written like this:

**assertEquals(String expected, String actual)assertEquals(String message, String expected, String actual)**

It is kind of counterintuitive, don't you think? So, in JUnit 5, this was changed so the method signatures look like this:

**assertEquals(String expected, String actual)assertEquals(String expected, String actual, String message)**

This was not only changed for **assertEquals(String, String)**, but also for all assertions that accept a custom message.

New assertions

There are a lot of new assertions in JUnit 5, such as assertThrows(), assertDoesNotThrow(), assertAll(), assertTimeout(), assertIterableEquals(), assertLinesMatch(), assertNotEquals(), and  assertTimeoutPreemptively().

In this post, we will be covering the ones that I think you'll use the most frequently: assertThrows() and assertAll().

assertThrows()

I'm pretty confident in saying that this is the best change made to Assertions in JUnit 5. This replaces the **@Test(expected = SomeException.class)** and the **@Rule ExpectedException** from JUnit 4. Let's see some code so we can understand it better:

Imagine that we have the following method:

**public void withdraw(double amount) {    if (amount < 0) {        throw new IllegalArgumentException("amount to be withdrawn must be greater than 0");    }    this.balance - amount;}**

So let's write a test that validates that our exception was thrown. In JUnit 4, we would write something like this:

**@Test(expected = IllegalArgumentException.class)public void withdrawShouldThrowExceptionWhenAmountIsLessThanZero() {    var bankAccount = new BankAccount(2000);    bankAccount.withdraw(-1);}**

Or, if we wanted to validate our exception message, we could write something like this:

**@Rulepublic ExpectedException expectedException = ExpectedException.none();@Testpublic void withdrawShouldThrowExceptionWhenAmountIsLessThanZero() {    expectedException.expect(IllegalArgumentException.class);    expectedException.expectMessage("amount to be withdrawn must be greater than 0");    var bankAccount = new BankAccount(2000);    bankAccount.withdraw(-1);}**

Now that our assertions are more powerful, we could write the same code below but enjoy greater readability and some new features. Let's take a look:

**@Testpublic void withdrawShouldThrowExceptionWhenAmountIsLessThanZero() {    IllegalArgumentException exception = assertThrows(        IllegalArgumentException.class,         () -> {             var bankAccount = new BankAccount(2000);            bankAccount.withdraw(-1);         },        "The IllegalArgumentException was not thrown, check your test."    );   assertEquals("amount to be withdrawn must be greater than 0", exception.getMessage(), "The exception message did not match the expected message")}**

Can you see how much easier it is now to understand what the test is doing? It also makes the most out of lambda and Java 8. Beyond this, the assertThrows make your code more flexible. You can read more about it in its [documentation](https://junit.org/junit5/docs/5.0.1/api/org/junit/jupiter/api/Assertions.html#assertThrows-java.lang.Class-org.junit.jupiter.api.function.Executable-java.lang.String-).

assertAll()

This is a new assertion that was added so that you can create a block of assertions. Now you could be asking: why would I want that if I can simply assert multiple times in my test method? Well, if you do that, the test will stop at the first failed assertion and you will not know the result of the next ones unless you make sure that the first assertion passes. With **assertAll(), **you make sure that every assertion runs, whether or not they pass, and you get a detailed explanation for each of the failures. Let's see an example:

**@Testpublic void validatePerson() {    var person  = myClass.getTheLastJedi();    assertAll("Should return Luke Skywalker",        () -> assertEquals("Lucas", person.getFirstName()),        () -> assertEquals("Skywalkar", person.getLastName()),        () -> assertEquals(35, person.getAge()),        () -> assertEquals(170, person.getHeightInCentimeters());}**

Now, even though two tests fail, all of the other ones will be executed and you will get something like the code below:

**org.opentest4j.MultipleFailuresError:    Should return Luke Skywalker (2 failures)    expected: <Luke> but was: <Lucas>    expected: <Skywalker> but was: <Skywalkar>**

# ![](https://lh6.googleusercontent.com/UIcELDQilsVrtEQgPLAmN9tiYnH3tJTTIDOd_zGbUK2DJzjcP5txwSYaQb0cNgbQUzqv3_e5-m98cDZ4IFZNosZa8ucj6m93GZZtFtc0weQTkJ7Chj5KDVfSEf8AwsFYZ3o7j2D6)

##### Conclusion

At first glance, JUnit 5 might look like it didn't change enough to justify migrating from the most used version, JUnit 4. But its changes do improve your test quality significantly, so you should consider migrating if you haven't already done so.

---

### Author

# Lucas Moraes

 Lucas Moraes is a Java Engineer at Avenue Code. He loves to automate everything he can. He also enjoys using the latest technologies that are available. In his free time, Lucas really likes to watch any kind of sports and is a huge Star Wars fan.

---

### Related Posts

### How to Build Unit Tests Using Jest

[READ MORE](https://blog.avenuecode.com/how-to-build-unit-tests-using-jest?hsLang=en-us)

### How Heuristics Can Improve Your Tests

[READ MORE](https://blog.avenuecode.com/how-heuristics-can-improve-your-tests?hsLang=en-us)

### How to Use WireMock for Integration Testing

[READ MORE](https://blog.avenuecode.com/how-to-use-wiremock-for-integration-testing?hsLang=en-us)

### The Best Way to Use Request Validation in Laravel REST API

[READ MORE](https://blog.avenuecode.com/the-best-way-to-use-request-validation-in-laravel-rest-api?hsLang=en-us)

### Leave a Comment!

### Avenue Code Social

[![Facebook Icon](https://blog.avenuecode.com/hubfs/Images/Blog/facebook.png?t=1486470796564)](https://www.facebook.com/avenuecode)

[![Twitter Icon](https://blog.avenuecode.com/hubfs/Images/Blog/twitter.png?t=1486470796842)](https://twitter.com/AvenueCode)

[![LinkedIn Icon](https://blog.avenuecode.com/hubfs/Images/Blog/linkedin.png?t=1486470796556)](https://www.linkedin.com/company/avenuecode/)

### Newsletter

Want to stay on top of all tips and news from Avenue Code?

### Popular Snippets

![Avenue Code-primary versions_logo white avenue code endorsement 2](https://blog.avenuecode.com/hs-fs/hubfs/Avenue%20Code%20New%20Logos%20-%202023/Avenue%20Code-primary%20versions_logo%20white%20avenue%20code%20endorsement%202.png?width=1180&name=Avenue%20Code-primary%20versions_logo%20white%20avenue%20code%20endorsement%202.png "Avenue Code-primary versions_logo white avenue code endorsement 2")

### About Us

- [Who We Are](https://www.avenuecode.com/who-we-are)
- [What We Do](https://www.avenuecode.com/what-we-do)
- [Portfolio](https://www.avenuecode.com/portfolio)
- [Partners](https://www.avenuecode.com/partners)
- [News](https://www.avenuecode.com/news)
- [Events](https://www.avenuecode.com/events)
- [Blog](https://blog.avenuecode.com/)
- [Contact](https://www.avenuecode.com/contact)

### Our Offices

San Francisco

[+1 415 766 4178](tel:+553125161448) [ac.inquiries@avenuecode.com](mailto:brazil.info@avenuecode.com)

Belo Horizonte

[+55 31 2516 1448](tel:+553125161448) [brazil.info@avenuecode.com](mailto:brazil.info@avenuecode.com)

São Paulo

[+55 11 3205 3232](tel:+553125161448) [brazil.info@avenuecode.com](mailto:brazil.info@avenuecode.com)

### We're Hiring!

- [Belo Horizonte](https://www.avenuecode.com/who-we-are)
- [New York](https://www.avenuecode.com/what-we-do)
- [San Francisco](https://www.avenuecode.com/portfolio)
- [São Paulo](https://www.avenuecode.com/partners)

---

©2015 - 2017 Avenue Code

[![Facebook Icon](https://blog.avenuecode.com/hubfs/Images/Icons/facebook-2.png)](https://www.facebook.com/avenuecode) [![Twitter Icon](https://blog.avenuecode.com/hubfs/Images/Icons/twitter-2.png)](https://twitter.com/AvenueCode) [![LinkedIn Icon](https://blog.avenuecode.com/hubfs/Images/Icons/linkedin-2.png)](https://www.linkedin.com/company/avenue-code) [![Glassdoor Icon](https://blog.avenuecode.com/hubfs/Images/Icons/glassdoor-icon-1.png)](https://www.glassdoor.com/Overview/Working-at-Avenue-Code-EI_IE456173.11,22.htm) [![YouTube Icon](https://blog.avenuecode.com/hubfs/Images/Icons/youtube-2.png)](https://www.youtube.com/user/AvenueCodePlay)

Please enable JavaScript to view the [comments powered by Disqus.](http://disqus.com/?ref_noscript)

© 2026 Avenue Code

```json
{
  "@context" : "https://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Lucas Moraes",
    "url" : "https://blog.avenuecode.com/author/lucas-moraes"
  },
  "dateModified" : "2021-05-05T17:00:01.045Z",
  "datePublished" : "2021-05-05T17:00:00.000Z",
  "headline" : "It's Time to Migrate: JUnit 5 is Better than JUnit 4",
  "image" : [ "https://blog.avenuecode.com/hubfs/E002F4B3-17E2-450F-9A73-D4525B33FD91_1_201_a.jpeg" ],
  "mainEntityOfPage" : {
    "@id" : "https://blog.avenuecode.com/its-time-to-migrate-junit5-is-better-than-junit4",
    "@type" : "WebPage"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://blog.avenuecode.com/hubfs/Avenue%20Code%20New%20Logos%20-%202023/Avenue%20Code-primary%20versions_LOGO%20HORIZONTAL%20group%201-7.png"
    },
    "name" : "Avenue Code"
  }
}
```