How to replace WireMock #Rule annotation in JUnit 5? - java

I'm using WireMock in my tests and have such a line of code:
#Rule
public WireMockRule wireMockRule = new WireMockRule(8080);
I want to switch to JUnit 5. So I added the next dependency (using Gradle):
testCompile('org.junit.jupiter:junit-jupiter-engine:5.1.1')
But there are no suggestions when I'm trying to import #Rule annotation.
Do I need to add another module of JUnit dependency? Or are rules not supported in JUnit 5? If not, how can I replace #Rule annotation to make tests work again?

In a general way, what you did with #Rule and #ClassRule in JUnit 4 should be done with #ExtendWith and Extension that associated provide a very close feature in JUnit 5.
It works as standards JUnit lifecycle hooks but that it is extracted in a Extension class. And similarly to #Rule, as many Extensions as required may be added for a test class.
To handle the issue you have several possible approaches among :
keep the JUnit 4 way (JUnit 5 owns the JUnit Vintage part that allows to execute JUnit 3 or 4 tests).
rewrite the #Rule as an Extension.
do the actual processing done by WireMockRule (start the server, execute your tests and stop the server) in each test of class with #BeforeEach and #AfterEach hook methods.
use a third library that implements the equivalent of WireMockRule in the JUnit 5 Extension way such as https://github.com/lanwen/wiremock-junit5
Note that your issue already discussed in the JUnit 5 Issues.

JUnit 4 annotations #Rule and #ClassRule do not exist in JUnit 5. Basically there is a new extension model that can be used to implement extensions with the same functionality. These extensions can be used with the #ExtendWith annotation.
There is a limited migration support for a subset of JUnit 4 rules in the junit-jupiter-migrationsupport module. Unfortunately, it's only limited to subclasses of ExternalResource and Verifier.
Before wiremock has official support for JUnit you have some workarounds:
Run JUnit 4 tests side by side with JUnit 5 tests with the junit-vintage-engine.
Start and stop the server yourself in the test code.
Use a 3rd party extension like wiremock-junit5 or wiremock-extension.

There is now official support for JUnit 5 Jupiter from WireMock 2.31.0.
Docs here: http://wiremock.org/docs/junit-jupiter/

The https://github.com/webcompere/java-test-gadgets project lets you solve this in a couple of ways.
You can use its support for JUnit 4 rules via the DangerousRuleAdapter - which will attempt to turn any JUnit 4 rule into a Plugin:
#ExtendWith(PluginExtension.class)
public class DangerousRuleAdapterExampleTest {
#Plugin
private DangerousRuleAdapter<WireMockRule> adapter =
new DangerousRuleAdapter<>(new WireMockRule());
#Test
void theTest() {
// use wiremock rule here
WireMockRule rule = adapter.get();
}
The rule adapters cannot work with rules that inspect the test class or the test method, but make a good attempt at running the rule.
There's also support for running a rule around some code:
TemporaryFolder temporaryFolder = new TemporaryFolder();
// let's use this temp folder with some test code
executeWithRule(temporaryFolder, () -> {
// here, the rule is _active_
callSomethingThatUses(temporaryFolder.getRoot());
});
And you can easily create your own new JUnit 5 plugin by using the PluginExtension and TestResource.of
#ExtendWith(PluginExtension.class)
class TestResourceIsActiveDuringTest {
private WireMockServer server;
#Plugin
private TestResource someResource = TestResource.from(() -> server.start(),
() -> server.stop());

From the JUnit 5 user guide:
#Rule and #ClassRule no longer exist; superseded by #ExtendWith and #RegisterExtension. See also "Limited JUnit 4 Rule Support".
However, as pointed out by Tom, WireMock has full JUnit Jupiter support since version 2.31.0:
// New JUnit 5 extension
#WireMockTest
class DeclarativeWireMockTest {
#Test
void test_something_with_wiremock(WireMockRuntimeInfo wmRuntimeInfo) {
// The static DSL will be automatically configured for you
stubFor(get("/static-dsl").willReturn(ok()));
// Instance DSL can be obtained from the runtime info parameter
WireMock wireMock = wmRuntimeInfo.getWireMock();
wireMock.register(get("/instance-dsl").willReturn(ok()));
// Info such as port numbers is also available
int port = wmRuntimeInfo.getHttpPort();
// Do some testing...
}
}
For more information, please refer to the corresponding docs.

Related

IntellIj not displaying exceptions from #BeforeAll JUnit5

In IntelliJ, with a Springboot project (2.6.6) using JUnit 5, given the following test class, IntelliJ doesn't display exceptions occuring in the #BeforeAll method.
#ExtendWith(SpringExtension.class)
public class SpringTest {
private Object OBJECT = new Object();
#BeforeAll
public static void before() {
throw new RuntimeException("SpringTest.RuntimeException"); // replace by actual business code
}
#Test
public void test() {
// Doesn't matter
assertNotNull(this.OBJECT);
}
Running the class within IntelliJ (only this class)
Running the package of the test
When the #BeforeAll method is more complex, it's hard to understand that an exception occured, until you run the whole test package by chance.
In JUnit 4 with a #Before method, IntelliJ was showing test failure, with the stacktrace of the exception (which makes things easier to debug).
My pom.xml only dependencies are spring-boot-starter & spring-boot-starter-test.
Is this a bug / Is there any solution to reproduce the old behaviour ?
Stack:
Spring boot 2.6.6 / JUnit 5
IntelliJ IDEA 2022.1 Build #IU-221.5080.210
OpenJDK_x64Temurin_11.0.14.1_1 / Maven-3.8.4
Edit
As pointed out in the comment, to reproduce #Before of JUnit4, you should use #BeforeEach in Junit 5. In this case, IntelliJ will behave as before and display exceptions even when running only the given test class.
For the #BeforeAll, question is still pending.
Edit 2
A bug has been opened for the #BeforeAll exceptions: https://youtrack.jetbrains.com/issue/IDEA-292662
Bug resolved in IntelliJ 2022.1.1 Preview.
See YouTrack: IDEA-292662

Issues with #Rule and PowerMockRule

I am using
#Rule
public PowerMockRule rule = new PowerMockRule();
in one of my test file. Now when I am removing this part of code several test cases starts failing which are in different folders and having no direct relationship with this file.
What could be the problem?
Refer to the below wiki link for more details.
Technically, instead of using #RunWith(PowerMockRunner.class) , you can use PowerMockRuleas shown in the below example.
So, when you remove PowerMockRule, your tests are gonna fail because you will not have #RunWith(PowerMockRunner.class) in your tests.
https://github.com/powermock/powermock/wiki/powermockrule
Bootstrapping using a JUnit Rule
Feature avaliable since PowerMock 1.4 Since version 1.4
It's possible to bootstrap PowerMock using a JUnit Rule instead of
using the PowerMockRunner and the RunWith
annotation. This allows you to use other JUnit runners while still
benefiting from PowerMock's functionality. You do this by specifying:
With PowerMockRule
#PrepareForTest(X.class);
public class MyTest {
#Rule
PowerMockRule rule = new PowerMockRule();
// Tests goes here
...
}
Without PowerMockRule
#RunWith(PowerMockRunner.class)
#PrepareForTest(X.class);
public class MyTest {
// Tests goes here
...
}

Run Junit test cases only from Test suite and not individual class

I have setup an embedded mongo via flapdoodle (de.flapdoodle.embed).
Quite a lot of mongo operations hence i would like to run all of them as a suite and setup the mongo just once in testsuite.
Now when i run the test cases via mvn install , it seems to run the test cases individually.
Is there a way to run test cases only from suite and not as a class.
baeldung.com describes the use of JUnit 5 Tags, which are very well suited for your case.
You can mark tests with two different tags:
#Test
#Tag("MyMongoTests")
public void testThatThisHappensWhenThatHappens() {
}
#Test
#Tag("MyTestsWithoutMongo")
public void testThatItDoesNotHappen() {
}
And execute either set in a suite, e.g.
#IncludeTags("MyMongoTests")
public class MyMongoTestSuite {
}
In your case, the tests could be categorized by whether Mongo is in the application context or not. So, theoretically, it might be possible to create a JUnit 5 Extension to add the tag. That would be the more complex solution though.

Selenide - Create new driver for each Junit 5 Test

I'm using Selenide with Junit 5
Selenide 5.1.0
Junit 5.3.2
When I run tests with junit, same driver/browser is used for all tests. I want to create new WebDriver instance for each test.
I can call driver.quit() in AfterEach method. But I wanted to know is there any inbuilt method to handle the same in Selenide. So that I won't have to worry about driver initialization.
It should open a new driver for each Test and close after execution
It appears that there is no official JUnit Jupiter (JUnit 5) integration from Selenide:
https://github.com/selenide/selenide/issues/488
However, you could implement an Extension in JUnit Jupiter similar to the one available for Selenium:
https://bonigarcia.github.io/selenium-jupiter/
There are JUnit5 extensions in Selenide: https://github.com/selenide/selenide/tree/master/statics/src/main/java/com/codeborne/selenide/junit5
One of them, BrowserStrategyExtension, can reopen browser before every test.
You can implement Junit5 AfterEachCallback.
Here's an example:
public class CloseBrowserAfterEachTest implements AfterEachCallback {
#Override
public void afterEach(ExtensionContext extensionContext) throws Exception {
WebDriverRunner.closeWebDriver();
}
}
And then annotate your test classes (or your base test class) with #ExtendWith({CloseBrowserAfterEachTest.class})

Exclude some JUnit tests from automated test suite

When writing code that interacts with external resources (such as using a web service or other network operation), I often structure the classes so that it can also be "stubbed" using a file or some other input method. So then I end up using the stubbed implementation to test other parts of the system and then one or two tests that specifically test calling the web service.
The problem is I don't want to be calling these external services either from Jenkins or when I run all of the tests for my project (e.g. "gradle test"). Some of the services have side effects, or may not be accessible to all developers.
Right now I just uncomment and then re-comment the #Test annotation on these particular test methods to enable and disable them. Enable it, run it manually to check it, then remember to comment it out again.
// Uncomment to test external service manually
//#Test
public void testSomethingExternal() {
Is there is a better way of doing this?
EDIT: For manual unit testing, I use Eclipse and am able to just right-click on the test method and do Run As -> JUnit test. But that doesn't work without the (uncommented) annotation.
I recommend using junit categories. See this blog for details : https://community.oracle.com/blogs/johnsmart/2010/04/25/grouping-tests-using-junit-categories-0.
Basically, you can annotate some tests as being in a special category and then you can set up a two test suites : one that runs the tests of that category and one that ignores tests in that category (but runs everything else)
#Category(IntegrationTests.class)
public class AccountIntegrationTest {
#Test
public void thisTestWillTakeSomeTime() {
...
}
#Test
public void thisTestWillTakeEvenLonger() {
....
}
}
you can even annotate individual tests"
public class AccountTest {
#Test
#Category(IntegrationTests.class)
public void thisTestWillTakeSomeTime() {
...
}
Anytime I see something manually getting turned on or off I cringe.
As far as I can see you use gradle and API for JUnit says that annotation #Ignore disables test. I will add gradle task which will add #Ignore for those tests.
If you're just wanting to disable tests for functionality that hasn't been written yet or otherwise manually disable some tests temporarily, you can use #Ignore; the tests will be skipped but still noted in the report.
If you are wanting something like Spring Profiles, where you can define rulesets for which tests get run when, you should either split up your tests into separate test cases or use a Filter.
You can use #Ignore annotation to prevent them from running automatically during test. If required, you may trigger such Ignored tests manually.
#Test
public void wantedTest() {
return checkMyFunction(10);
}
#Ignore
#Test
public void unwantedTest() {
return checkMyFunction(11);
}
In the above example, unwantedTest will be excluded.

Categories

Resources