Skip to content

Commit 0c121fa

Browse files
committed
Update example to Java 8, and JUnit 5
1 parent 9fcaf13 commit 0c121fa

File tree

7 files changed

+255
-113
lines changed

7 files changed

+255
-113
lines changed

README.md

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,28 @@
1-
java-maven-junit-helloworld
2-
===========================
1+
#A Java/Maven/JUnit HelloWorld example
32

43
A „Hello World!” sample written in Java using Maven for the build, that showcases a few very simple tests.
54

65
This example demonstrates:
76

8-
* Unit tests written with [JUnit 4](http://junit.org/)
9-
* Unit test using [PowerMockito](https://code.google.com/p/powermock/) to mock classes and test `System.exit()`
10-
* Integration tests written with [JUnit 4](http://junit.org/)
11-
* Integration test using [system-rules](http://www.stefan-birkner.de/system-rules/) to test `System.out`
12-
* Code coverage reports via [Cobertura](http://cobertura.github.io/cobertura/)
7+
* A simple Java 8 application with tests
8+
* Unit tests written with [JUnit 5](https://junit.org/junit5/)
9+
* Integration tests written with [JUnit 5](https://junit.org/junit5/)
10+
* Code coverage reports via [JaCoCo](https://www.jacoco.org/jacoco/)
1311
* A Maven build that puts it all together
1412

15-
Running the tests
16-
-----------------
13+
## Running the tests
1714

1815
* To run the unit tests, call `mvn test`
1916
* To run the integration tests as well, call `mvn verify`
20-
* To generate (unit test) code coverage reports, call `mvn cobertura:cobertura`, and point a browser at the output in `target/site/cobertura/`
17+
* Code coverage reports are generated when `mvn verify` (or a full `mvn clean install`) is called.
18+
Point a browser at the output in `target/site/jacoco-both/index.html` to see the report.
2119

22-
Conventions
23-
-----------
20+
## Conventions
2421

2522
This example follows the following basic conventions:
2623

27-
| unit test | integration test
28-
--- | --- | ---
29-
__resides in:__ | `src/test/java/*Test.java` | `src/test/java/*IT.java`
30-
__executes in Maven phase:__ | test | verify
31-
__handled by Maven plugin:__ | [surefire](http://maven.apache.org/surefire/maven-surefire-plugin/) | [failsafe](http://maven.apache.org/surefire/maven-failsafe-plugin/)
24+
| | unit test | integration test |
25+
| --- | --- | --- |
26+
| **resides in:** | `src/test/java/*Test.java` | `src/test/java/*IT.java` |
27+
| **executes in Maven phase:** | test | verify |
28+
| **handled by Maven plugin:** | [surefire](http://maven.apache.org/surefire/maven-surefire-plugin/) | [failsafe](http://maven.apache.org/surefire/maven-failsafe-plugin/) |

pom.xml

Lines changed: 108 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,79 +7,87 @@
77

88
<groupId>com.example</groupId>
99
<artifactId>java-maven-junit-helloworld</artifactId>
10-
<version>1.0-SNAPSHOT</version>
10+
<version>2.0-SNAPSHOT</version>
1111
<packaging>jar</packaging>
1212

1313
<properties>
1414
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1515
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
16+
17+
<!-- This configures the compiler to produce Java 8 class files. -->
18+
<!-- The minimum JDK version installed is 8 of course, but newer JDK releases should work too. -->
19+
<maven.compiler.source>1.8</maven.compiler.source>
20+
<maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
21+
22+
<junit.jupiter.version>5.2.0</junit.jupiter.version>
23+
<junit.platform.version>1.2.0</junit.platform.version>
24+
<hamcrest.version>1.3</hamcrest.version>
25+
<mockito.version>2.21.0</mockito.version>
26+
<jacoco.plugin.version>0.8.1</jacoco.plugin.version>
1627
</properties>
1728

1829
<dependencies>
1930
<!-- Testing dependencies. -->
2031
<dependency>
21-
<groupId>junit</groupId>
22-
<artifactId>junit</artifactId>
23-
<version>4.11</version>
32+
<groupId>org.junit.jupiter</groupId>
33+
<artifactId>junit-jupiter-api</artifactId>
34+
<version>${junit.jupiter.version}</version>
2435
<scope>test</scope>
2536
</dependency>
2637
<dependency>
27-
<groupId>org.powermock</groupId>
28-
<artifactId>powermock-module-junit4</artifactId>
29-
<version>1.5.1</version>
38+
<groupId>org.junit.jupiter</groupId>
39+
<artifactId>junit-jupiter-params</artifactId>
40+
<version>${junit.jupiter.version}</version>
3041
<scope>test</scope>
3142
</dependency>
3243
<dependency>
33-
<groupId>org.powermock</groupId>
34-
<artifactId>powermock-api-mockito</artifactId>
35-
<version>1.5.1</version>
44+
<groupId>org.junit.jupiter</groupId>
45+
<artifactId>junit-jupiter-engine</artifactId>
46+
<version>${junit.jupiter.version}</version>
3647
<scope>test</scope>
3748
</dependency>
3849
<dependency>
39-
<groupId>com.github.stefanbirkner</groupId>
40-
<artifactId>system-rules</artifactId>
41-
<version>1.4.0</version>
42-
<scope>test</scope>
43-
<!-- junit:junit-dep is deprecated, and junit:junit replaces it. -->
44-
<exclusions>
45-
<exclusion>
46-
<groupId>junit</groupId>
47-
<artifactId>junit-dep</artifactId>
48-
</exclusion>
49-
</exclusions>
50+
<groupId>org.hamcrest</groupId>
51+
<artifactId>hamcrest-core</artifactId>
52+
<version>${hamcrest.version}</version>
53+
</dependency>
54+
<dependency>
55+
<groupId>org.mockito</groupId>
56+
<artifactId>mockito-junit-jupiter</artifactId>
57+
<version>${mockito.version}</version>
5058
</dependency>
5159
</dependencies>
5260

5361
<build>
5462
<plugins>
63+
5564
<plugin>
5665
<!-- Configures the compiler. -->
5766
<groupId>org.apache.maven.plugins</groupId>
5867
<artifactId>maven-compiler-plugin</artifactId>
59-
<version>3.1</version>
68+
<version>3.8.0</version>
6069
<configuration>
61-
<source>1.6</source>
62-
<target>1.6</target>
6370
<compilerArgs>
6471
<arg>-Xlint</arg>
6572
</compilerArgs>
6673
</configuration>
6774
</plugin>
75+
6876
<plugin>
6977
<!-- Unit tests are run by surefire. -->
7078
<!-- Classes under src/test/java called *Test are included automatically. -->
71-
<!-- Integration tests are run during the test phase. -->
79+
<!-- Unit tests are run during the test phase. -->
7280
<groupId>org.apache.maven.plugins</groupId>
7381
<artifactId>maven-surefire-plugin</artifactId>
74-
<version>2.16</version>
82+
<version>2.22.0</version>
7583
</plugin>
7684
<plugin>
7785
<!-- Integration tests are run by failsafe. -->
7886
<!-- Classes under src/test/java called *IT are included automatically. -->
7987
<!-- Integration tests are run during the verify phase. -->
8088
<groupId>org.apache.maven.plugins</groupId>
8189
<artifactId>maven-failsafe-plugin</artifactId>
82-
<version>2.16</version>
90+
<version>2.22.0</version>
8391
<executions>
8492
<execution>
8593
<goals>
@@ -89,18 +97,81 @@
8997
</execution>
9098
</executions>
9199
</plugin>
92-
</plugins>
93-
</build>
94100

95-
<reporting>
96-
<plugins>
97101
<plugin>
98-
<!-- JUnit code coverage. -->
99-
<groupId>org.codehaus.mojo</groupId>
100-
<artifactId>cobertura-maven-plugin</artifactId>
101-
<version>2.6</version>
102+
<!-- Code coverage plugin. -->
103+
<!-- This sets up code coverage for the unit tests, the integration tests, -->
104+
<!-- and the combined coverage of both. -->
105+
<!-- After running a `mvn clean install`, open `target/site/jacoco-both/index.html` in a browser. -->
106+
<groupId>org.jacoco</groupId>
107+
<artifactId>jacoco-maven-plugin</artifactId>
108+
<version>${jacoco.plugin.version}</version>
109+
<executions>
110+
<execution>
111+
<id>jacoco-prepare-agent</id>
112+
<goals>
113+
<goal>prepare-agent</goal>
114+
</goals>
115+
</execution>
116+
<execution>
117+
<id>jacoco-prepare-agent-integration</id>
118+
<goals>
119+
<goal>prepare-agent-integration</goal>
120+
</goals>
121+
</execution>
122+
<execution>
123+
<id>jacoco-report</id>
124+
<goals>
125+
<goal>report</goal>
126+
</goals>
127+
</execution>
128+
<execution>
129+
<id>jacoco-check</id>
130+
<goals>
131+
<goal>check</goal>
132+
</goals>
133+
<configuration>
134+
<rules />
135+
</configuration>
136+
</execution>
137+
<execution>
138+
<!-- Combine the code coverage statistics generated for the unit and the integration tests. -->
139+
<id>jacoco-merge</id>
140+
<goals>
141+
<goal>merge</goal>
142+
</goals>
143+
<phase>verify</phase>
144+
<configuration>
145+
<fileSets>
146+
<fileSet>
147+
<directory>${project.build.directory}</directory>
148+
<includes>
149+
<include>*.exec</include>
150+
</includes>
151+
</fileSet>
152+
</fileSets>
153+
<destFile>${project.build.directory}/jacoco-both.exec</destFile>
154+
</configuration>
155+
</execution>
156+
<execution>
157+
<id>jacoco-integration</id>
158+
<goals>
159+
<goal>report-integration</goal>
160+
</goals>
161+
</execution>
162+
<execution>
163+
<id>jacoco-reportboth</id>
164+
<goals>
165+
<goal>report</goal>
166+
</goals>
167+
<configuration>
168+
<dataFile>${project.build.directory}/jacoco-both.exec</dataFile>
169+
<outputDirectory>${project.reporting.outputDirectory}/jacoco-both</outputDirectory>
170+
</configuration>
171+
</execution>
172+
</executions>
102173
</plugin>
103-
</plugins>
104-
</reporting>
105174

175+
</plugins>
176+
</build>
106177
</project>

src/main/java/com/example/javamavenjunithelloworld/Hello.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
public class Hello {
99

1010
static final String HELLO = "Hello!";
11+
1112
public static final int MAXIMUM_AMOUNT_OF_TIMES = 20;
12-
private short times = 1;
13+
14+
private int times = 1;
1315

1416
/**
1517
* Set how many times "Hello!" should be said.
@@ -19,10 +21,12 @@ public class Hello {
1921
*/
2022
public void setTimes(int times) {
2123
if (times < 0 || times > MAXIMUM_AMOUNT_OF_TIMES) {
22-
throw new IllegalArgumentException("Parameter «times» should be a positive integer no larger than "
23-
+ MAXIMUM_AMOUNT_OF_TIMES + ".");
24+
throw new IllegalArgumentException(String.format(
25+
"Parameter «times» should be a positive number no larger than %d.",
26+
MAXIMUM_AMOUNT_OF_TIMES
27+
));
2428
}
25-
this.times = (short) times;
29+
this.times = times;
2630
}
2731

2832
/**

src/test/java/com/example/javamavenjunithelloworld/HelloAppTest.java

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
11
package com.example.javamavenjunithelloworld;
22

3-
import org.junit.Test;
4-
import org.junit.runner.RunWith;
5-
import org.powermock.api.mockito.PowerMockito;
6-
import org.powermock.core.classloader.annotations.PrepareForTest;
7-
import org.powermock.modules.junit4.PowerMockRunner;
3+
import com.example.javamavenjunithelloworld.TestingSecurityManager.TestExitException;
4+
import org.junit.jupiter.api.AfterAll;
5+
import org.junit.jupiter.api.BeforeAll;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.extension.ExtendWith;
8+
import org.mockito.junit.jupiter.MockitoExtension;
9+
10+
import static org.hamcrest.CoreMatchers.is;
11+
import static org.hamcrest.MatcherAssert.assertThat;
12+
import static org.junit.jupiter.api.Assertions.fail;
813

9-
import static org.mockito.Mockito.*;
10-
import static org.powermock.api.mockito.PowerMockito.whenNew;
1114

1215
/**
1316
* Unit test for HelloApp.
1417
* <p/>
1518
* A unit test aims to test all code and code paths of a specific class.
16-
* <p/>
17-
* This test uses PowerMock and Mockito to mock objects.
1819
*/
19-
@RunWith(PowerMockRunner.class)
20-
@PrepareForTest({System.class, HelloApp.class})
20+
@ExtendWith(MockitoExtension.class)
2121
public class HelloAppTest {
22+
static SecurityManager originalSecurityManager;
23+
24+
@BeforeAll
25+
public static void setup() {
26+
// Insert our own custom SecurityManager that throws an exception when System.exit() is called.
27+
originalSecurityManager = System.getSecurityManager();
28+
System.setSecurityManager(new TestingSecurityManager());
29+
}
30+
31+
@AfterAll
32+
public static void tearDown() {
33+
// Reinsert the original SecurityManager now that we are done with these tests.
34+
System.setSecurityManager(originalSecurityManager);
35+
}
2236

2337
@Test
2438
public void testMain() {
@@ -27,34 +41,32 @@ public void testMain() {
2741
}
2842

2943
@Test
30-
public void testWrongArgument() {
31-
PowerMockito.mockStatic(System.class);
32-
44+
public void testBogusArgument() {
3345
String[] args = {"bicycle"};
34-
HelloApp.main(args);
3546

36-
// Did the program exit with the expected error code?
37-
PowerMockito.verifyStatic(only());
38-
System.exit(HelloApp.EXIT_STATUS_PARAMETER_NOT_UNDERSTOOD);
47+
try {
48+
HelloApp.main(args);
49+
// Our custom SecurityManager should have thrown an exception when HelloApp exited.
50+
// This means this line below cannot be reached. To make sure that our custom SecurityManager
51+
// works as expected, we fail the test if this line is ever reached:
52+
fail("Unreachable.");
53+
} catch (TestExitException e) {
54+
// Did the program exit with the expected error code?
55+
assertThat(e.getStatus(), is(HelloApp.EXIT_STATUS_PARAMETER_NOT_UNDERSTOOD));
56+
}
3957
}
4058

4159
@Test
42-
public void testHelloError() throws Exception {
43-
PowerMockito.mockStatic(System.class);
44-
45-
// Mock Hello used by HelloApp to throw the expected exception when invoked with setTimes(5).
46-
Hello hi = mock(Hello.class);
47-
doThrow(new IllegalArgumentException("Nope.")).when(hi).setTimes(5);
48-
// Sneakily insert our fake Hello class when it is created.
49-
whenNew(Hello.class).withNoArguments().thenReturn(hi);
50-
51-
// We know this will raise the expected exception, because we mocked Hello.
52-
String[] args = {"5"};
53-
HelloApp.main(args);
60+
public void testTooHighArgument() {
61+
String[] args = {"999"};
5462

55-
// Did the program exit with the expected error code?
56-
PowerMockito.verifyStatic(only());
57-
System.exit(HelloApp.EXIT_STATUS_HELLO_FAILED);
63+
try {
64+
HelloApp.main(args);
65+
fail("Unreachable.");
66+
} catch (TestExitException e) {
67+
// Did the program exit with the expected error code?
68+
assertThat(e.getStatus(), is(HelloApp.EXIT_STATUS_HELLO_FAILED));
69+
}
5870
}
5971

6072
@Test

0 commit comments

Comments
 (0)