IntelliJ Idea: JUnit Test Class template depending on condition - java

I often write tests of different types. Depending on test type it might have different setup.
For instance all my service tests have the following annotation under class declaration:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = NONE)
All controller tests have these annotations:
#RunWith(SpringRunner.class)
#WithMockUser
#WebMvcTest(controllers = MyController.class)
Another tests have different setup, etc.
Every time when I create a test I have to copy-paste this part from another test.
I'm looking for a solution that will help me automate this process in IntelliJ Idea.
Q: Is there any way to define JUnit Test Class templates which work differently depending on the type of the test?
Let's say class name ends with word "Service" - its generated test should use one template, if class name ends with "Controller" - its test should use another one, etc.
It is also possible to detect test type by package name or some other conditions like class content.

The JUnit code generation templates can be found under Settings > File And Code templates > Code.
You can't really create separate code templates, but what you could do is add logic to the existing templates. They use Velocity based directives.
So if, for example, we take the existing JUnit 4 template:
import static org.junit.Assert.*;
#parse("File Header.java")
public class ${NAME} {
${BODY}
}
We can modify it to the following:
import static org.junit.Assert.*;
#if($CLASS_NAME.contains("Service"))
//Import whatever you need for services here.
#end
#if($CLASS_NAME.contains("Controller"))
//Import whatever you need for controllers here.
#end
#parse("File Header.java")
#if($CLASS_NAME.contains("Controller"))
#set($CLASS_SUFFIX = ".class" )
#RunWith(SpringRunner.class)
#RunWithMock
#WebMvcTest(controllers = $CLASS_NAME$CLASS_SUFFIX)
#end
#if($CLASS_NAME.contains("Service"))
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = NONE)
#end
public class ${NAME} {
${BODY}
}
This way if you generate a new JUnit 4 test class through the context menu (hit alt-enter on the class name of the class you want to test and generate new test) it will generate different output if the name of the class to test contains 'Controller' or 'Service'. You might want to change that to endswith instead of contains depending on whatever naming conventions you use.
I've left out the actual import statements in both cases, but I'm sure you'll be able to add those.

Related

Junit - Testsuite - Classnames from file

I have a junit test suite as below and it works as expected.
#RunWith(Suite.class)
#Suite.SuiteClasses({
abc.class,
xyz.class
})
public class RunSuiteIT {}
Now my requirement is to keep the class names (abc,xyz) in a json file and get it read during run time. Is it possible to do that in Java/JUnit?
Yes, you should be able to do this at runtime, which means you can't use an annotation. Instead you will need a static Suite suite() method that reads the JSON file and uses the reflection API to create the Class instances from the string input.

JUnit: is it possible to create a Test Suite that executes all test of classes that share a naming convention?

I am aware that I can make a TestSuite enumerating all the classes that I want, for example:
#RunWith(Suite.class)
#SuiteClasses({SQLServerTests1.class, SQLServerTest2.class, ... })
public class AllSQLServerTests {}
However I have almost 100+ classes and I don't want to have to remember to include any new one in the #SuiteClasses annotation.
As my classes have a naming convention (starting with "SQLServer" for example) I am searching for a way to do something like this:
#RunWith(Suite.class)
#SuiteClasses(prefix="SQLServer")
public class AllSQLServerTests {}
is it possible with plain JUnit? with spring or any other framework?
Tag'em
You can add many tags to each test or test class:
#Test
#Tag("red")
#Tag("production")
public void testWithColour() {...}
#RunWith(JUnitPlatform.class)
#IncludeTags("red & !production")
public class JUnit5Example {
//...
}
You can also use #ExcludeTags but it cannot co-exist with #IncludeTags
Run all in test package
#RunWith(JUnitPlatform.class)
#SelectPackages("com.acme.megaproduct.slowtests")
public class JUnit5Example {
//...
}
Write custom Test Runner
Perhaps none of the above can acommodate your needs, in which case you can add custom filtering by writing your own runner.
See here for step by step how to do it.
Then you just use it like:
#RunWith(MyCustomRunner.class)
public class CustomTestSuite {
//...
}

Run methods annotated with #Category from IntelliJ IDEA

I have a test class with multiple test methods that I would like to group by some criteria. For this purpose, using JUnit's #Category annotation on a method level seemed like a fine solution:
public class TestClass {
#Test
#Category(AssignmentServiceCategory.class)
public void testMethod1() {}
#Test
#Category(OtherCategory.class)
public void testMethod2() {}
}
I would like to create different run configurations in IntelliJ IDEA for those separate categories so that only the test methods annotated with certain category are executed. My configuration looks like this:
However, when I run this, all of the tests from the class where the method is declared are run, instead of only the ones annotated with specified category. Is my configuration incorrect, or does IDEA allow only class-level #Category annotations?
Versions:
IntelliJ IDEA 2018.1 (181.4203.550)
JRE: 1.8.0_152-release-1136-b20 amd64
JUnit 4.12
UPDATED
I tried to reproduce the issue and could not.
Here's my Test Class
package com.mytests.category;
import org.junit.Test;
import org.junit.experimental.categories.Category;
public class MyTest {
#Test
#Category(PerformanceTests.class)
public void testMethod1() {
System.out.println("method1");
}
#Test
#Category(RegressionTests.class)
public void testMethod2() {
System.out.println("method2");
}
}
Make sure you have the necessary interfaces. In JUnit, you need to create marker interfaces to represent the categories:
package com.mytests.category;
public interface RegressionTests {}
and
package com.mytests.category;
public interface PerformanceTests {}
Then in IntelliJ, I ran the tests once and it creates a configuration for me automatically. Then I edit the configuration
The results were as expected:
Only testMethod1 was executed.
OLDER ANSWER
Or from IntelliJ's Doc (https://www.jetbrains.com/help/idea/run-debug-configuration-junit.html)
Category Select this option if you only want to run test classes and
test methods that are annotated either with the category given with
the #IncludeCategory annotation, or a subtype of this category. Fill in the following fields:
Category Specify the desired category. Type category name, or click
browseButton and select the desired category in the dialog that opens.
Or You could create a TestSuite and specify (in there) which categories the suite is to include.
Something like
package org.mytests.category;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
#RunWith(Categories.class)
#Categories.IncludeCategory(RegressionTests.class)
#Suite.SuiteClasses({ClassA.class, ClassB.class, ClassC.class})
public class RegressionTestSuite {
}
I have (had) the same problem with Intellij 2020.1.2
There seems to be a bug related to what setting you select for search for tests.
If i choose in single module all the test are executed, even the ones annotated with other Categories.
If I choose any of the other options (in whole project, across module dependencies) it simply works as expected..

Private class unit tests

How to unit test private (means with package visibility) classed in java?
I have a package, only one class is public in this package, other classes are private. How to cover other classed with unit tests? I would like to include unit tests in resting jar.
Create the unit test class in the same package.
For example, If com.example.MyPrivateClass located in src/main/java/com/example/MyPrivateClass.java
Then the test class will be in same package com.example.MyPrivateClassTestCase and will be located in src/test/java/com/example/MyPrivateClassTestCase.java
There are two ways to do this.
The standard way is to define your test class in the same package of the class to be tested. This should be easily done as modern IDE generates test case in the same package of the class being tested by default.
The non-standard but very useful way is to use reflection. This allows you to define private methods as real "private" rather than "package private". For example, if you have class.
class MyClass {
private Boolean methodToBeTested(String argument) {
........
}
}
You can have your test method like this:
class MyTestClass {
#Test
public void testMethod() {
Method method = MyClass.class.getDeclaredMethod("methodToBeTested", String.class);
method.setAccessible(true);
Boolean result = (Boolean)method.invoke(new MyClass(), "test parameter");
Assert.assertTrue(result);
}
}
As indicated in #Kowser's answer, the test can be in the same package.
In Eclipse, and I assume other IDEs, one can have classes in different projects but in the same package. A project can be declared to depend on another project, making the other project's classes available. That permits a separate unit test project that depends on the production project and follows its package structure, but has its own root directory.
That structure keeps the test code cleanly separated from production code.

Grouping JUnit tests

Is there any way to group tests in JUnit, so that I can run only some groups?
Or is it possible to annotate some tests and then globally disable them?
I'm using JUnit 4, I can't use TestNG.
edit: #RunWith and #SuiteClasses works great. But is it possible to annotate like this only some tests in test class? Or do I have to annotate whole test class?
JUnit 4.8 supports grouping:
public interface SlowTests {}
public interface IntegrationTests extends SlowTests {}
public interface PerformanceTests extends SlowTests {}
And then...
public class AccountTest {
#Test
#Category(IntegrationTests.class)
public void thisTestWillTakeSomeTime() {
...
}
#Test
#Category(IntegrationTests.class)
public void thisTestWillTakeEvenLonger() {
...
}
#Test
public void thisOneIsRealFast() {
...
}
}
And lastly,
#RunWith(Categories.class)
#ExcludeCategory(SlowTests.class)
#SuiteClasses( { AccountTest.class, ClientTest.class })
public class UnitTestSuite {}
Taken from here: https://community.oracle.com/blogs/johnsmart/2010/04/25/grouping-tests-using-junit-categories-0
Also, Arquillian itself supports grouping:
https://github.com/weld/core/blob/master/tests-arquillian/src/test/java/org/jboss/weld/tests/Categories.java
Do you want to group tests inside a test class or do you want to group test classes? I am going to assume the latter.
It depends on how you are running your tests. If you run them by Maven, it is possible to specify exactly what tests you want to include. See the Maven surefire documentation for this.
More generally, though, what I do is that I have a tree of test suites. A test suite in JUnit 4 looks something like:
#RunWith(Suite.class)
#SuiteClasses({SomeUnitTest1.class, SomeUnitTest2.class})
public class UnitTestsSuite {
}
So, maybe I have a FunctionTestsSuite and a UnitTestsSuite, and then an AllTestsSuite which includes the other two. If you run them in Eclipse you get a very nice hierarchical view.
The problem with this approach is that it's kind of tedious if you want to slice tests in more than one different way. But it's still possible (you can for example have one set of suites that slice based on module, then another slicing on the type of test).
To handle the globally disabling them, JUnit (4.5+) has two ways One is to use the new method assumeThat. If you put that in the #BeforeClass (or the #Before) of a test class, and if the condition fails, it will ignore the test. In the condition you can put a system property or something else that can be globally set on or off.
The other alternative is to create a custom runner which understands the global property and delegates to the appropriate runner. This approach is a lot more brittle (since the JUnit4 internal runners are unstable and can be changed from release to release), but it has the advantage of being able to be inherited down a class hierarchy and be overridden in a subclass. It is also the only realistic way to do this if you have to support legacy JUnit38 classes.
Here is some code to do the custom Runner. Regarding what getAppropriateRunnerForClass might do, the way I implemented it was to have a separate annotation that tells the custom runner what to run with. The only alternative was some very brittle copy paste from the JUnit code.
private class CustomRunner implements Runner
private Runner runner;
public CustomRunner(Class<?> klass, RunnerBuilder builder) throws Throwable {
if (!isRunCustomTests()) {
runner = new IgnoredClassRunner(klass);
} else {
runner = getAppropriateRunnerForClass(klass, builder);
}
public Description getDescription() {
return runner.getDescription();
}
public void run(RunNotifier notifier) {
runner.run(notifier);
}
}
EDIT: The #RunWith tag only works for a whole class. One way to work around that limiation is to move the test methods into a static inner class and annotate that. That way you have the advantage of the annotation with the organization of the class. But, doing that won't help with any #Before or #BeforeClass tags, you will have to recreate those in the inner class. It can call the outer class's method, but it would have to have its own method as a hook.
In JUnit 5 you can declare #Tag for filtering tests, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4
From the javadoc :
tags are used to filter which tests are executed for a given test
plan. For example, a development team may tag tests with values such
as "fast", "slow", "ci-server", etc. and then supply a list of tags to
be used for the current test plan, potentially dependent on the
current environment.
For example you could declare a test class with a "slow" #Tag that will be inherited for all methods and override it for some methods if required :
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
#Tag("slow")
public class FooTest{
//
#Test
void loadManyThings(){
...
}
#Test
void loadManyManyThings(){
...
}
#Test
#Tag("fast")
void loadFewThings(){
...
}
}
You could apply the same logic for other test classes.
In this way test classes (and methods too) belongs to a specific tag.
As a good practice instead of copying and pasting #Tag("fast") and #Tag("slow") throughout the test classes, you can create custom composed annotations.
For example :
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Tag;
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
#Tag("slow")
public #interface Slow {
}
and use it as :
#Test
#Slow
void slowProcessing(){
...
}
To enable or disable test marked with a specific tag during the text execution you can rely on the maven-surefire-plugin documentation :
To include tags or tag expressions, use groups.
To exclude tags or tag expressions, use either excludedGroups.
Just configure in your pom.xml the plugin according to your requirement (example of the doc) :
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<groups>acceptance | !feature-a</groups>
<excludedGroups>integration, regression</excludedGroups>
</configuration>
</plugin>
</plugins>
</build>
For information the test goal documentation is not updated.
Try JUnit Test Groups. From documentation :
#TestGroup("integration")
public class MyIntegrationTest {
#ClassRule
public static TestGroupRule rule = new TestGroupRule();
...
}
Execute a simple test group: -Dtestgroup=integration
Execute multiple test groups: -Dtestgroup=group1,group2
Execute all test groups: -Dtestgroup=all
You can create test Suite objects that contain groups of tests. Alternatively, your IDE (like Eclipse) may have support for running all the tests contained in a given package.
You can Use Test Suite(http://qaautomated.blogspot.in/2016/09/junit-test-suits-and-test-execution.html) or you can Junit Categories(http://qaautomated.blogspot.in/2016/09/junit-categories.html) for grouping your test cases effectively.

Categories

Resources