I'd like my CI build to fail when the tests write something to System.out or System.err. Preferably I would like to have a list of tests which produced unwanted output.
I tried to use mavens surefire plugin with an added listener, but that does only part of the trick as you can see from the question JUnit RunListener is removed on fail()
Did someone else try something like this and are there other ways to achieve a cleaner console on CI builds?
You might have a look at this Maven plugin https://github.com/policeman-tools/forbidden-apis which check for forbidden API calls. You can define wich calls should be forbidden in the code.
edit Simple example to show exclusion of not permitted api calls. In the example the call of System.out.println should not be permitted in test classes with the exception of one test class.
pom.xml
<build>
<plugins>
<plugin>
<groupId>de.thetaphi</groupId>
<artifactId>forbiddenapis</artifactId>
<version>2.0</version>
<executions>
<execution>
<goals>
<goal>check</goal>
<goal>testCheck</goal>
</goals>
</execution>
</executions>
<configuration>
<signaturesFiles>
<signaturesFile>${project.build.testOutputDirectory}/signatures.txt</signaturesFile>
</signaturesFiles>
<excludes>
<!-- specify the classes which should be excluded -->
<exclude>**/Permitted*</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
The signatur file src\test\resources\signatures.txt
java.io.PrintStream#println(java.lang.String)
Test classes
// the one which doesn't use System.out
package sub.optimal;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Assert;
import org.junit.Test;
public class NoSystemOutTest {
#Test
public void dummy() {
Logger logger = Logger.getGlobal();
logger.log(Level.INFO, "some info");
Assert.assertTrue(true);
}
}
.
// the one in which the use of System.out should be permitted
package sub.optimal;
import org.junit.Assert;
import org.junit.Test;
public class PermittedSystemOutTest {
#Test
public void dummy() {
System.out.println("permitted usage");
Assert.assertTrue(true);
}
}
.
// the one in which the use of System.out is not permitted
package sub.optimal;
import org.junit.Assert;
import org.junit.Test;
public class NotPermittedSystemOutTest {
#Test
public void dummy() {
System.out.println("not permitted usage");
Assert.assertTrue(true);
}
}
Running the check with mvn test-compile forbiddenapis:testCheck will only report the forbidden api usage in NotPermittedSystemOutTest.
[ERROR] Forbidden method invocation: java.io.PrintStream#println(java.lang.String)
[ERROR] in sub.optimal.NotPermittedSystemOutTest (NotPermittedSystemOutTest.java:9)
You can use the Checkstyle plugin so that your test with unwanted code fails and will be listed as failed in surfire reports.
You can check whether a test writes to System.out by using the library System Rules
public class YourTest {
#Rule
public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
#After
public void assertNoTextHasBeenWrittenToSystemOut() {
assertEquals("", systemOutRule.getLog())
}
...
}
There same may be done with System.err by using the SystemErrRule.
Related
The #Nested classes that are executed in JUnit 5 they are ordered to run AFTER all the tests in eclosing class. How could I enforce the same behavior using maven, if my goal is to run a single enclosing class and it's nested classes? Is there a commandline or pom.xml modification to make this example test pass?
package example;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
public class SomeTopLevelTest {
private static boolean nestedDone = false;
#Test
void test1() {
Assertions.assertFalse(nestedDone,
"Nested classes should execute after the enclosing tests");
}
#Nested
class SomeNestedTest {
#Test
void test2() {
nestedDone = true;
}
}
#AfterAll
static void recheck() {
Assertions.assertTrue(nestedDone, "Nested class should be executed");
}
}
This does pass in IDE:
But does not in commanline, if I try to specify the name:
mvn test -Dtest=example.SomeTopLevelTest
[ERROR] Failures:
[ERROR] SomeTopLevelTest.recheck:27 Nested class should be executed ==> expected: <true> but was: <false>
mvn test -Dtest=example.SomeTopLevelTest*
[ERROR] Failures:
[ERROR] SomeTopLevelTest.test1:14 Nested classes should execute after the enclosing tests ==> expected: <false> but was: <true>
The problem of #Nested classes not executing is a known issue and it has been reported in both JUnit5 and Surefire issue trackers, but as of now remains unresolved.
The current state of affairs (tested with Maven 3.6.3, Junit5 5.7.2, Surefire 2.22.2 up to 3.0.0-M5):
A. Not selecting a test
mvn test
Results in executing all test classes as expected: methods from enclosing classes first, and then methods from #Nested classes, level by level
B. Selecting test the standard way
mvn test -Dtest=example.SomeTopLevelTest
This apparently triggers the default surefire excludes that use the following pattern:
<excludes>
<exclude>**/*$*</exclude>
</excludes>
Why does it not happen in case A is a mystery, but one can override this behaviour by explicitly clearing the excludes pattern:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude/>
</excludes>
</configuration>
</plugin>
It does not seem to be possible to do without modyfing the pom.xml.
This DOES NOT solve the issue as posted in this question, because the nested classes are still executed first.
C. Using wildcard with -Dtest parameter
mvn test -Dtest=example.SomeTopLevelTest*
This explicitly selects all the nested classes, but - as stated in the question - results in executing the nested classes first, so it's not a solution.
D. Using includes
mvn test -DtestName=example.SomeTopLevelTest
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>${testName}</include>
</includes>
</configuration>
</plugin>
Apparently include patterns work quite differently than -Dtest parameter, because this is finally the solution to pass the test from the question. With this setup the testName may be a single class, wildcard pattern or regex
example.SomeTopLevelTest to execute all test methods in single class
example/* - all tests (including nested) in package example, but not sub-packages
example/** - all tests in package and subpackages
advanced regex, is supprted too
How can QuarkusTestResource be used in conjunction with Tag Annotation?
Example Test Routine
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
#QuarkusTest
#Tag("integration")
#QuarkusTestResource(DatabaseResource.class)
public class MyTest {
#Test
public void () {
doTests...
}
}
Maven Snippet:
<quarkus-plugin.version>1.12.1.Final</quarkus-plugin.version>
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>1.12.1.Final</quarkus.platform.version>
<surefire-plugin.version>2.22.1</surefire-plugin.version>
<testscope>unit</testscope>
...
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<groups>${testscope}</groups>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
...
Maven Command:
./mvnw clean test
Result: QuarkusTestResource are started even if no QuarkusTest is annotated with "unit", so it seems that Quarkus is not aware of the Tag Annotation?
Quarkus Test Resources are global which means it will run anyway, even if your class is annotated with a tag that shouldn't run.
To prevent this, try annotating your class with
#QuarkusTestResource(restrictToAnnotatedClass = true)
From the Quarkus' website
test resources are global, even if they are defined on a test class or custom profile, which means they will all be activated for all tests, even though we do remove duplicates. If you want to only enable a test resource on a single test class or test profile, you can use #QuarkusTestResource(restrictToAnnotatedClass = true).
What you are looking for is likely the tags methof of QuarkusTestProfile. See this part of the documentation
I'm not an advanced Java programmer, but I want to create a Discord messenger bot. In order to do that, I need to add additional dependencies to my application. I chose Maven instead of Gradle.
For now, I try to run a basic program:
package com.b.pokebot;
import javax.security.auth.login.LoginException;
import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.MessageChannel;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
import net.dv8tion.jda.core.exceptions.RateLimitedException;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
public class PokeBot extends ListenerAdapter {
public static void main(String[] args)
{
}
#Override
public void onMessageReceived(MessageReceivedEvent e) {
//Obtains properties of the received message
Message objMsg = e.getMessage();
MessageChannel objChannel = e.getChannel();
User objUser = e.getAuthor();
//Responds to any user who says "hello"
if (objMsg.getContent().equals("hello")) {
objChannel.sendMessage("Hello, " + objUser.getAsMention() +"!").queue();
}
}
}
I added maven-jar-plugin to my pom.xml under the tag:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<version>3.0.2</version>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.b.pokebot.PokeBot</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
Tried to add addClassPath to as well.
For now, I cannot run the program with command
java -jar target/pokebot-1.0-SNAPSHOT.jar
because of error:
Error: Could not find or load main class com.b.pokebot.PokeBot
Even though there is a class in that package...
If I remove the maven-jar-plugin entry, I get a similar error, but instead of class name, I get my jar file name.
Could someone tell me how to fix that, so I can run the program from command line?
When I run it from Netbeans, there is no problem...
btw: I altered package name - the "b" is a longer word in the actual code.
I run my JUnit and Mockito tests in a big project. I use them for testing my server-side components that connect to web-service. All these connections require some time and it is not neccessary for them to be executed during the build.
I would like that my tests would be ignored during the build.
I have about 10 classes with tests. So the obvious way is to annotate all the classes with #Ignore. However I should do this every time I commit my code to the project and then re-annotate all tests. Not the very best solution I think.
So is this possible somehow simply ignore all package (let say com.example.tests) with the tests?
Or what might be the solution to ignore tests in the build in a simple way?
You can mention on your build.gradle what packages to exclude from tests
test {
exclude '**/*IntegrationTest*'
}
same for maven:
must consider this notation:
By default, the Surefire Plugin will automatically include all test classes with the following wildcard patterns:
"**/Test*.java" - includes all of its subdirectories and all Java filenames that start with "Test".
"**/*Test.java" - includes all of its subdirectories and all Java filenames that end with "Test".
"**/*Tests.java" - includes all of its subdirectories and all Java filenames that end with "Tests".
"**/*TestCase.java" - includes all of its subdirectories and all Java filenames that end with "TestCase".
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<excludes>
<exclude>*com.example.tests*/*Test.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>
Another option is the old
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
or even when call it
mvn install -DskipTests
Using Categories seems to be an option that can come in handy
This is how you may add these to your Gradle script.
test {
useJUnit {
includeCategories 'org.gradle.junit.CategoryA'
excludeCategories 'org.gradle.junit.CategoryB'
}
}
A sample can be found here, adding it for a quick reference.
public interface FastTests
{
/* category marker */
}
public interface SlowTests
{
/* category marker */
}
public class A
{
#Category(SlowTests.class)
#Test public void a()
{
}
}
#Category(FastTests.class})
public class B
{
#Test public void b()
{
}
}
#RunWith(Categories.class)
#IncludeCategory(SlowTests.class)
#ExcludeCategory(FastTests.class)
#SuiteClasses({ A.class, B.class })
public class SlowTestSuite
{
}
I have found the solution for my case.
To disable all the tests during the build or even in any other context that you want the Spring annotation #IfProfileValue can be used. All tests with this annotation will be executed only in wanted context.
The example is this:
#IfProfileValue(name="enableTests", value="true")
public class DemoApplicationTests {
#Test
public void contextLoads() {
...
}
}
In my IDE I can edit the configuration and set the variable by:
-DenableTests=true
This annotation can be used on the level of a class or on the level of a test also.
All classes or tests annotated with such #IfProfileValue will be executed only in my environment and will be ignored during the build.
This approach is the best for me because it is not convenient in my project to change main pom.xml for my own test needs.
Addition.
Also in Spring or Spring Boot you should add Runner.
For example in Spring:
#RunWith(SpringJUnit4ClassRunner.class)
#IfProfileValue(name="enableTests", value="true")
#ContextConfiguration(classes = { YourClassConfig.class })
YourClassConfig might be empty:
#Configuration
public class YourClassConfig {
}
I have some unit test which depends on environment variable.
I'd like to unset these environment variable before testing using maven.
Question: How can I achieve that?
Unfortunately, in this case you cannot use the environmentVariables option of the Maven Surefire Plugin, mainly because it would only work to add new environment variables but not override (or reset, which is actually equals to override to empty value) an existing variable.
Also note: an ant run wrapped in Maven and executed before the test would not work either.
The proposed solution is based on Java, especially on this approach proposed in another SO post. Although not advisable for application code, this hack may be acceptable for test code.
You could add the following class to your test code (under src/test/java):
package com.sample;
import java.lang.reflect.Field;
import java.util.Map;
public class EnvHack {
#SuppressWarnings("unchecked")
public static void resetEnvironmentVariable(String name, String value) throws Exception {
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
theEnvironmentField.setAccessible(true);
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
env.put(name, value);
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass
.getDeclaredField("theCaseInsensitiveEnvironment");
theCaseInsensitiveEnvironmentField.setAccessible(true);
Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
cienv.put(name, value);
}
public static void main(String[] args) throws Exception {
resetEnvironmentVariable("test_prop", "test_value");
}
}
The class above is basically hacking Java API to change in memory the values of the environment variables. As such, they can be set to different values, reset and even unset actually (remove it from the map).
Since the class is now part of your test code, you have several options:
Use the class in the #Before methods (or #BeforeClass, with a certain difference) of a certain JUnit test case (before every JUnit method of the concerned class)
Use it within a JUnit test method (custom and narrowed usage)
Run its main method before any executed JUnit test (in a more global way) as explained below (and probably answering the question, even though other scenarios are also worth to mention, imho).
Let's have a look at each possible solution.
Use the class in the #Before methods of a certain JUnit test case
package com.sample;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class MainTest {
#Before
public void init() throws Exception {
EnvHack.resetEnvironmentVariable("test_prop", "test_value");
}
#Test
public void testEnvProperty() throws Exception {
String s = System.getenv("test_prop");
Assert.assertEquals(s, "test_value");
}
}
This solution can be used per test class and when a set of tests (methods) share the same requirements (suggestion: if they don't, it may be an hint, probably some method should be moved out).
Use it within a JUnit test method
package com.sample;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class MainTest {
#Before
public void init() throws Exception {
EnvHack.resetEnvironmentVariable("test_prop", "test_value");
}
#Test
public void testEnvProperty() throws Exception {
EnvHack.resetEnvironmentVariable("test_prop", "test_value2");
String s = System.getenv("test_prop");
Assert.assertEquals(s, "test_value2");
}
}
This solution gives the highest freedom: you can play with exactly the concerned variable exactly where required, although may suffer of code duplication it could also enforce tests isolation.
Run its main method before any executed JUnit test
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<phase>process-test-classes</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>com.sample.EnvHack</mainClass>
<classpathScope>test</classpathScope>
</configuration>
</execution>
</executions>
</plugin>
Note what we are doing in this case:
We are invoking the java goal of the Exec Maven Plugin to actually invoke the mainClass of our env hack class.
We are invoking it with classPathScope set to test in order to make it visible to the Enforcer Plugin
We are running it as part of the process-test-classes phase just to make sure it is executed before the test phase and hence before any test.
This solution centralizes the whole prepare environment procedure, once for all tests.
On the other side, you may also consider to use mocking in your tests. This is not a centralized option (unless you code it) but could give further hints and hence worth to mention. Here is a sample code resetting an environment variable via PowerMock
package com.sample;
import org.junit.*;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(System.class)
public class EnvTest {
#Before
public void init() {
PowerMockito.mockStatic(System.class);
Mockito.when(System.getenv("TEST_PROP")).thenReturn("TEST_VALUE");
}
#Test
public void test() {
String var = System.getenv("TEST_PROP");
Assert.assertEquals("TEST_VALUE", var);
}
}
This is similar to the first and second approach proposed above.
Note that to make it work you need to add the following dependencies to your POM:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
</dependencies>
General note: indeed having tests which are not fully isolated and depends on the existence (or the absence) of an environment variable is not advisable. You may run into maintenance issues or have more nasty troubleshooting, for colleagues or for your future yourself. So, if you really need it, better to properly document it.