:testDir: ../../../../src/test/java

[[migrating-from-junit4]]
== Migrating from JUnit 4

Although the JUnit Jupiter programming model and extension model do not support JUnit 4
features such as `Rules` and `Runners` natively, it is not expected that source code
maintainers will need to update all of their existing tests, test extensions, and custom
build test infrastructure to migrate to JUnit Jupiter.

Instead, JUnit provides a gentle migration path via a _JUnit Vintage test engine_ which
allows existing tests based on JUnit 3 and JUnit 4 to be executed using the JUnit Platform
infrastructure. Since all classes and annotations specific to JUnit Jupiter reside under
the `org.junit.jupiter` base package, having both JUnit 4 and JUnit Jupiter in the
classpath does not lead to any conflicts. It is therefore safe to maintain existing JUnit
4 tests alongside JUnit Jupiter tests. Furthermore, since the JUnit team will continue to
provide maintenance and bug fix releases for the JUnit 4.x baseline, developers have
plenty of time to migrate to JUnit Jupiter on their own schedule.


[[migrating-from-junit4-running]]
=== Running JUnit 4 Tests on the JUnit Platform

Make sure that the `junit-vintage-engine` artifact is in your test runtime path. In that
case JUnit 3 and JUnit 4 tests will automatically be picked up by the JUnit Platform
launcher.

See the example projects in the {junit-examples-repo}[`junit-examples`] repository to
find out how this is done with Gradle and Maven.

[[migrating-from-junit4-categories-support]]
==== Categories Support

For test classes or methods that are annotated with `@Category`, the _JUnit Vintage test
engine_ exposes the category's fully qualified class name as a <<running-tests-tags, tag>>
for the corresponding test class or test method. For example, if a test method is
annotated with `@Category(Example.class)`, it will be tagged with `"com.acme.Example"`.
Similar to the `Categories` runner in JUnit 4, this information can be used to filter the
discovered tests before executing them (see <<running-tests>> for details).

[[migrating-from-junit4-parallel-execution]]
=== Parallel Execution

The JUnit Vintage test engine supports parallel execution of top-level test classes and test methods,
allowing existing JUnit 3 and JUnit 4 tests to benefit from improved performance through
concurrent test execution. It can be enabled and configured using the following
<<running-tests-config-params, configuration parameters>>:

`junit.vintage.execution.parallel.enabled=true|false`::
  Enable/disable parallel execution (defaults to `false`). Requires opt-in for `classes`
  or `methods` to be executed in parallel using the configuration parameters below.

`junit.vintage.execution.parallel.classes=true|false`::
  Enable/disable parallel execution of test classes (defaults to `false`).

`junit.vintage.execution.parallel.methods=true|false`::
  Enable/disable parallel execution of test methods (defaults to `false`).

`junit.vintage.execution.parallel.pool-size=<number>`::
  Specifies the size of the thread pool to be used for parallel execution. By default, the
  number of available processors is used.

[[migrating-from-junit4-parallel-execution-class-level]]
==== Parallelization at Class Level

Let's assume we have two test classes `FooTest` and `BarTest` with each class containing
three unit tests. Now, let's enable parallel execution of test classes:

[source,properties]
----
junit.vintage.execution.parallel.enabled=true
junit.vintage.execution.parallel.classes=true
----

With this setup, the `VintageTestEngine` will use two different threads,
one for each test class:

[source,plaintext]
----
ForkJoinPool-1-worker-1 - BarTest::test1
ForkJoinPool-1-worker-2 - FooTest::test1
ForkJoinPool-1-worker-1 - BarTest::test2
ForkJoinPool-1-worker-2 - FooTest::test2
ForkJoinPool-1-worker-1 - BarTest::test3
ForkJoinPool-1-worker-2 - FooTest::test3
----

[[migrating-from-junit4-parallel-execution-method-level]]
==== Parallelization at Method Level

Alternatively, we can enable parallel test execution at a method level,
rather than the class level:

[source,properties]
----
junit.vintage.execution.parallel.enabled=true
junit.vintage.execution.parallel.methods=true
----

Therefore, the test methods within each class will be executed in parallel, while
different test classes will be executed sequentially:

[source,plaintext]
----
ForkJoinPool-1-worker-1 - BarTest::test1
ForkJoinPool-1-worker-2 - BarTest::test2
ForkJoinPool-1-worker-3 - BarTest::test3

ForkJoinPool-1-worker-3 - FooTest::test1
ForkJoinPool-1-worker-2 - FooTest::test2
ForkJoinPool-1-worker-1 - FooTest::test3
----

[[migrating-from-junit4-parallel-execution-class-and-method-level]]
==== Full Parallelization

Finally, we can also enable parallelization at both class and method level:

[source,properties]
----
junit.vintage.execution.parallel.enabled=true
junit.vintage.execution.parallel.classes=true
junit.vintage.execution.parallel.methods=true
----

With these properties set, the `VintageTestEngine` will execute all tests classes and
methods in parallel, potentially significantly reducing the overall test suite execution time:

[source,plaintext]
----
ForkJoinPool-1-worker-6 - FooTest::test2
ForkJoinPool-1-worker-7 - BarTest::test3
ForkJoinPool-1-worker-3 - FooTest::test1
ForkJoinPool-1-worker-8 - FooTest::test3
ForkJoinPool-1-worker-5 - BarTest::test2
ForkJoinPool-1-worker-4 - BarTest::test1
----

[[migrating-from-junit4-parallel-execution-pool-size]]
==== Configuring the Pool Size

The default thread pool size is equal to the number of available processors. However, we
can also configure the pool size explicitly:

[source,properties]
----
junit.vintage.execution.parallel.enabled=true
junit.vintage.execution.parallel.classes=true
junit.vintage.execution.parallel.methods=true
junit.vintage.execution.parallel.pool-size=4
----

For instance, if we update our previous example that uses full parallelization and
configure the pool size to four, we can expect to see our six test methods executed with
a parallelism of four:

[source,plaintext]
----
ForkJoinPool-1-worker-2 - FooTest::test1
ForkJoinPool-1-worker-4 - BarTest::test2
ForkJoinPool-1-worker-3 - BarTest::test1
ForkJoinPool-1-worker-4 - BarTest::test3
ForkJoinPool-1-worker-2 - FooTest::test2
ForkJoinPool-1-worker-3 - FooTest::test3
----

As we can see, even though we set the thread pool size was four, only three threads were
used in this case. This happens because the pool adjusts the number of active threads
based on workload and system needs.

[[migrating-from-junit4-parallel-execution-disabled]]
==== Sequential Execution

On the other hand, if we disable parallel execution, the `VintageTestEngine`
will execute all tests sequentially, regardless of the other properties:

[source,properties]
----
junit.vintage.execution.parallel.enabled=false
junit.vintage.execution.parallel.classes=true
junit.vintage.execution.parallel.methods=true
----

Similarly, tests will be executed sequentially if you enable parallel execution in general
but enable neither class-level nor method-level parallelization.

[[migrating-from-junit4-tips]]
=== Migration Tips

The following are topics that you should be aware of when migrating existing JUnit 4
tests to JUnit Jupiter.

* Annotations reside in the `org.junit.jupiter.api` package.
* Assertions reside in `org.junit.jupiter.api.Assertions`.
  - Note that you may continue to use assertion methods from `org.junit.Assert` or any
    other assertion library such as {AssertJ}, {Hamcrest}, {Truth}, etc.
* Assumptions reside in `org.junit.jupiter.api.Assumptions`.
  - Note that JUnit Jupiter 5.4 and later versions support methods from JUnit 4's
    `org.junit.Assume` class for assumptions. Specifically, JUnit Jupiter supports JUnit
    4's `AssumptionViolatedException` to signal that a test should be aborted instead of
    marked as a failure.
* `@Before` and `@After` no longer exist; use `@BeforeEach` and `@AfterEach` instead.
* `@BeforeClass` and `@AfterClass` no longer exist; use `@BeforeAll` and `@AfterAll`
  instead.
* `@Ignore` no longer exists: use `@Disabled` or one of the other built-in
  <<writing-tests-conditional-execution, execution conditions>> instead
  - See also <<migrating-from-junit4-ignore-annotation-support>>.
* `@Category` no longer exists; use `@Tag` instead.
* `@RunWith` no longer exists; superseded by `@ExtendWith`.
  - For `@RunWith(Enclosed.class)` use `@Nested`.
  - For `@RunWith(Parameterized.class)` see <<migrating-from-junit4-tips-parameterized>>.
* `@Rule` and `@ClassRule` no longer exist; superseded by `@ExtendWith` and
  `@RegisterExtension`.
  - See also <<migrating-from-junit4-rule-support>>.
* `@Test(expected = ...)` and the `ExpectedException` rule no longer exist; use
  `Assertions.assertThrows(...)` instead.
  - See <<migrating-from-junit4-rule-support>> if you still need to use
    `ExpectedException`.
* Assertions and assumptions in JUnit Jupiter accept the failure message as their last
  argument instead of the first one.
  - See <<migrating-from-junit4-failure-message-arguments>> for details.

[[migrating-from-junit4-tips-parameterized]]
==== Parameterized test classes

Unless `@UseParametersRunnerFactory` is used, a JUnit 4 parameterized test class can be
converted into a JUnit Jupiter
<<writing-tests-parameterized-tests, `@ParameterizedClass`>> by following these steps:

. Replace `@RunWith(Parameterized.class)` with `@ParameterizedClass`.
. Add a class-level `@MethodSource("methodName")` annotation where `methodName` is the
  name of the method annotated with `@Parameters` and remove the `@Parameters` annotation
  from the method.
. Replace `@BeforeParam` and `@AfterParam` with `@BeforeParameterizedClassInvocation` and
  `@AfterParameterizedClassInvocation`, respectively, if there are any methods with such
  annotations.
. Change the imports of the `@Test` and `@Parameter` annotations to use the
  `org.junit.jupiter.params` package.
. Change assertions etc. to use the `org.junit.jupiter.api` package as usual.
. Optionally, remove all `public` modifiers from the class and its methods and fields.

====
[source,java,indent=0]
.Before
----
include::{testDir}/example/ParameterizedMigrationDemo.java[tags=before]
----

[source,java,indent=0]
.After
----
include::{testDir}/example/ParameterizedMigrationDemo.java[tags=after]
----
====

[[migrating-from-junit4-rule-support]]
=== Limited JUnit 4 Rule Support

As stated above, JUnit Jupiter does not and will not support JUnit 4 rules natively. The
JUnit team realizes, however, that many organizations, especially large ones, are likely
to have large JUnit 4 code bases that make use of custom rules. To serve these
organizations and enable a gradual migration path the JUnit team has decided to support a
selection of JUnit 4 rules verbatim within JUnit Jupiter. This support is based on
adapters and is limited to those rules that are semantically compatible to the JUnit
Jupiter extension model, i.e. those that do not completely change the overall execution
flow of the test.

The `junit-jupiter-migrationsupport` module from JUnit Jupiter currently supports the
following three `Rule` types including subclasses of these types:

* `org.junit.rules.ExternalResource` (including `org.junit.rules.TemporaryFolder`)
* `org.junit.rules.Verifier` (including `org.junit.rules.ErrorCollector`)
* `org.junit.rules.ExpectedException`

As in JUnit 4, Rule-annotated fields as well as methods are supported. By using these
class-level extensions on a test class such `Rule` implementations in legacy code bases
can be _left unchanged_ including the JUnit 4 rule import statements.

This limited form of `Rule` support can be switched on by the class-level annotation
`{EnableRuleMigrationSupport}`. This annotation is a _composed annotation_ which enables
all rule migration support extensions: `VerifierSupport`, `ExternalResourceSupport`, and
`ExpectedExceptionSupport`. You may alternatively choose to annotate your test class with
`@EnableJUnit4MigrationSupport` which registers migration support for rules _and_ JUnit
4's `@Ignore` annotation (see <<migrating-from-junit4-ignore-annotation-support>>).

However, if you intend to develop a new extension for JUnit Jupiter please use the new
extension model of JUnit Jupiter instead of the rule-based model of JUnit 4.


[[migrating-from-junit4-ignore-annotation-support]]
=== JUnit 4 @Ignore Support

In order to provide a smooth migration path from JUnit 4 to JUnit Jupiter, the
`junit-jupiter-migrationsupport` module provides support for JUnit 4's `@Ignore`
annotation analogous to Jupiter's `{Disabled}` annotation.

To use `@Ignore` with JUnit Jupiter based tests, configure a _test_ dependency on the
`junit-jupiter-migrationsupport` module in your build and then annotate your test class
with `@ExtendWith(IgnoreCondition.class)` or `{EnableJUnit4MigrationSupport}` (which
automatically registers the `IgnoreCondition` along with
<<migrating-from-junit4-rule-support>>). The `IgnoreCondition` is an
`{ExecutionCondition}` that disables test classes or test methods that are annotated with
`@Ignore`.

[source,java,indent=0]
----
include::{testDir}/example/IgnoredTestsDemo.java[tags=user_guide]
----


[[migrating-from-junit4-failure-message-arguments]]
=== Failure Message Arguments

The `Assumptions` and `Assertions` classes in JUnit Jupiter declare arguments in a
different order than in JUnit 4. In JUnit 4 assertion and assumption methods accept the
failure message as the first argument; whereas, in JUnit Jupiter assertion and assumption
methods accept the failure message as the last argument.

For instance, the method `assertEquals` in JUnit 4 is declared as `assertEquals(String
message, Object expected, Object actual)`, but in JUnit Jupiter it is declared as
`assertEquals(Object expected, Object actual, String message)`. The rationale for this is
that a failure message is _optional_, and optional arguments should be declared after
required arguments in a method signature.

The methods affected by this change are the following:

- Assertions
  * `assertTrue`
  * `assertFalse`
  * `assertNull`
  * `assertNotNull`
  * `assertEquals`
  * `assertNotEquals`
  * `assertArrayEquals`
  * `assertSame`
  * `assertNotSame`
  * `assertThrows`
- Assumptions
  * `assumeTrue`
  * `assumeFalse`
