├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE.md ├── README.md ├── abstract.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mvnw ├── mvnw.cmd ├── pom.xml ├── slides ├── 02_dynamic_tests │ ├── dynamic_tests.md │ ├── dynamic_tests_1.png │ ├── dynamic_tests_2.png │ ├── dynamic_tests_3.png │ ├── static_tests_1.png │ ├── static_tests_2.png │ └── static_tests_3.png ├── 05_modularization │ ├── modularization.md │ ├── modularization_1.png │ ├── modularization_2.png │ ├── one_jar.pdf │ └── usage_stats.png ├── 06_roadmap │ └── roadmap.md └── junit5.png └── src ├── main └── java │ └── org │ └── junit │ └── junit5workshop │ ├── _2_dynamic_tests │ └── PrimeNumbers.java │ └── extensions │ ├── BenchmarkExtension.java │ ├── DisabledOnFriday.java │ ├── DisabledOnFridayCondition.java │ ├── DisabledOnOs.java │ ├── DisabledOnOsCondition.java │ └── SimpleBenchmarkExtension.java └── test ├── java └── org │ └── junit │ └── junit5workshop │ ├── HelloWorldTest.java │ ├── _1_basics │ ├── _1_FirstTest.java │ ├── _2_LifecycleTest.java │ ├── _3_AssertionTest.java │ ├── _4_AssumptionTest.java │ └── _5_NestedAndNamedTest.java │ ├── _2_dynamic_tests │ └── PrimeNumbersTest.java │ └── _3_extensions │ ├── _1_DisabledOnFriday.java │ ├── _2_DisabledOnOS.java │ ├── _3_SimpleBenchmark.java │ └── _4_Benchmark.java └── resources └── primes-1000.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Maven 2 | /target 3 | 4 | # Gradle 5 | .gradle 6 | build/ 7 | 8 | # Ignore Gradle GUI config 9 | gradle-app.setting 10 | 11 | # Eclipse 12 | /.classpath 13 | /.settings/ 14 | /.project 15 | /bin/ 16 | 17 | # IntelliJ 18 | .idea 19 | *.iml 20 | *.ipr 21 | *.iws 22 | 23 | # Misc 24 | *.log 25 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © `` `` 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JUnit 5 Workshop 2 | 3 | Slides, examples, and exercises for a JUnit 5 Workshop, initially held by [Marc Philipp](http://twitter.com/marcphilipp) and [Nicolai Parlog](https://twitter.com/nipafx) at [JUG Karlsruhe on January 25th 2017](http://jug-karlsruhe.de/content/junit-workshop/). 4 | 5 | ## Outline 6 | 7 | 1. introduction (Nicolai, 45 minutes) 8 | * [setup](#setup) 9 | * [basic features](https://codefx-org.github.io/talk-junit-5/#/_basics) (`@Test`, lifecycle, ...) 10 | * simple [tasks](src/test/java/org/junit/junit5workshop/_1_basics) to write some tests 11 | 2. dynamic tests (Marc, 30 minutes) 12 | * [theory](slides/02_dynamic_tests/dynamic_tests.md) 13 | * [exercise](src/test/java/org/junit/junit5workshop/_2_dynamic_tests) to create dynamic tests 14 | 3. extension mechanism ([theory](https://codefx-org.github.io/talk-junit-5/#/_extensions); Nicolai, 15 minutes) 15 | 16 | 30 minute break 17 | 18 | 4. extension mechanism ([practice](src/test/java/org/junit/junit5workshop/_3_extensions,) Nicolai, 30 minutes) 19 | 5. architecture. modularization, side-by-side use of JUnit 4 and 5 ([theory](slides/05_modularization/modularization.md); [example](https://github.com/junit-team/junit5-workshop/tree/junit-platform-runner); Marc, 15 minutes) 20 | 6. roadmap to GA ([theory](slides/06_roadmap/roadmap.md); Marc, 10 minutes) 21 | 7. Q & A & more hacking, open end... 22 | 23 | ## Setup 24 | 25 | First, clone the project: 26 | 27 | ```bash 28 | git clone https://github.com/junit-team/junit5-workshop.git 29 | ``` 30 | 31 | JUnit-5-specific plugins for **Maven** Surefire and **Gradle** are included in the project's `pom.xml` and `build.gradle` so both tools work without additional setup. 32 | Try it with `mvn clean test` or `gradle test`. Alternatively, you can use the provided Gradle or Maven wrappers with `./mvnw clean test` or `./gradlew test`. 33 | 34 | **IntelliJ** is the first (and so far only) tool that has native support and everything should work out of the box. 35 | Since this project uses JUnit 5 Milestone 4 [2017.1.2](https://blog.jetbrains.com/idea/2017/04/intellij-idea-2017-1-2-update-is-available/) is required to run the tests. 36 | 37 | **Eclipse** requires a little fiddling to get to work but it is possible with 4.7 M4, following [these steps](https://bugs.eclipse.org/bugs/show_bug.cgi?id=488566#c8). 38 | -------------------------------------------------------------------------------- /abstract.md: -------------------------------------------------------------------------------- 1 | # Abstract 2 | 3 | Nach der Crowdfunding-Kampagne "JUnit Lambda" in 2015 und einigen Milestone-Releases in 2016 wird das Final Release von JUnit 5 für 2017 erwartet. 4 | Hierbei handelt es sich um eine grundlegend überarbeitete Version. 5 | 6 | In diesem Workshop werden wir gemeinsam Tests schreiben, die die neue JUnit Jupiter API verwenden, die mit JUnit 5 eingeführt werden wird. 7 | Ausgehend von den Basis-Features, werden wir uns eingehend mit dynamischen Tests und dem neuen Extension-Modell beschäftigen. 8 | Wir werden die neue modulare Architektur einführen und sehen, welche Vorteile sie bietet. 9 | Die Kompatibilität mit vorherigen JUnit-Versionen sowie die Möglichkeiten zur schrittweisen Migration auf die neue API werden weitere Themen sein. 10 | All dies wird im Wechsel aus kurzen Theorie- und längeren Praxisabschnitten geschehen. 11 | 12 | Vorraussetzungen: 13 | Aktuelle IntelliJ-, oder Eclipse-Version mit eingerichtetem JDK. 14 | Wenn Tests aus der IDE ausgeführt werden sollen, mindestens IntelliJ 2016.3.1 bzw. Eclipse 4.7 M4 mit [dieser Erweiterung](https://bugs.eclipse.org/bugs/show_bug.cgi?id=488566#c4). 15 | 16 | ## Nicolai Parlog (CodeFX) 17 | 18 | Nicolai ist selbständiger Softwareentwicker, Autor und Trainer. Er lebt in Karlsruhe und bloggt auf [codefx.org](http://codefx.org/) und [sitepoint.com/java](https://sitepoint.com/java). 19 | 20 | ## Marc Philipp (Citrix/GetGo Germany GmbH) 21 | 22 | Marc Philipp arbeitet als Softwareentwickler bei Citrix/GetGo in Karlsruhe. Er ist langjähriger Maintainer von JUnit, Mitinitiator der Crowdfunding-Kampagne "JUnit Lambda" und JUnit's "Keeper of the Green Bar". 23 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | } 4 | 5 | buildscript { 6 | repositories { 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-M4' 11 | } 12 | } 13 | 14 | apply plugin: 'java' 15 | apply plugin: 'eclipse' 16 | apply plugin: 'idea' 17 | apply plugin: 'org.junit.platform.gradle.plugin' 18 | 19 | jar { 20 | baseName = 'junit5-workshop' 21 | version = '1.0.0-SNAPSHOT' 22 | } 23 | 24 | compileTestJava { 25 | sourceCompatibility = 1.8 26 | targetCompatibility = 1.8 27 | options.compilerArgs += '-parameters' 28 | } 29 | 30 | dependencies { 31 | // this is all you need to write tests with JUnit Jupiter; 32 | // for writing tests "testCompile" scope would suffice, but extensions 33 | // are defined in the project's "main" folder, so we need "compile" 34 | // JUnit Jupiter API and TestEngine implementation 35 | compile("org.junit.jupiter:junit-jupiter-api:5.0.0-M4") 36 | 37 | // other test libraries work out of the box. e.g. AssertJ and Mockito 38 | testCompile("org.assertj:assertj-core:3.3.0") 39 | testCompile("org.mockito:mockito-core:1.10.19") 40 | 41 | // contains the engine that actually runs the Jupiter-tests 42 | testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0-M4") 43 | } 44 | 45 | task wrapper(type: Wrapper) { 46 | gradleVersion = '3.3' 47 | } 48 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jan 11 13:56:14 CET 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # traverses directory structure from process work directory to filesystem root 188 | # first directory with .mvn subdirectory is considered project base directory 189 | find_maven_basedir() { 190 | local basedir=$(pwd) 191 | local wdir=$(pwd) 192 | while [ "$wdir" != '/' ] ; do 193 | if [ -d "$wdir"/.mvn ] ; then 194 | basedir=$wdir 195 | break 196 | fi 197 | wdir=$(cd "$wdir/.."; pwd) 198 | done 199 | echo "${basedir}" 200 | } 201 | 202 | # concatenates all lines of a file 203 | concat_lines() { 204 | if [ -f "$1" ]; then 205 | echo "$(tr -s '\n' ' ' < "$1")" 206 | fi 207 | } 208 | 209 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 210 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 211 | 212 | # For Cygwin, switch paths to Windows format before running java 213 | if $cygwin; then 214 | [ -n "$M2_HOME" ] && 215 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 216 | [ -n "$JAVA_HOME" ] && 217 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 218 | [ -n "$CLASSPATH" ] && 219 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 220 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 221 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 222 | fi 223 | 224 | # Provide a "standardized" way to retrieve the CLI args that will 225 | # work with both Windows and non-Windows executions. 226 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 227 | export MAVEN_CMD_LINE_ARGS 228 | 229 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 230 | 231 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@ 232 | exec "$JAVACMD" \ 233 | $MAVEN_OPTS \ 234 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 235 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 236 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 237 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %* 125 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 126 | if ERRORLEVEL 1 goto error 127 | goto end 128 | 129 | :error 130 | set ERROR_CODE=1 131 | 132 | :end 133 | @endlocal & set ERROR_CODE=%ERROR_CODE% 134 | 135 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 136 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 137 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 138 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 139 | :skipRcPost 140 | 141 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 142 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 143 | 144 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 145 | 146 | exit /B %ERROR_CODE% 147 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | org.junit 7 | junit5-workshop 8 | 1.0-SNAPSHOT 9 | 10 | 11 | 12 | 13 | org.junit.jupiter 14 | junit-jupiter-api 15 | 5.0.0-M4 16 | 18 | compile 19 | 20 | 21 | 22 | 23 | 24 | org.assertj 25 | assertj-core 26 | 3.3.0 27 | test 28 | 29 | 30 | 31 | org.mockito 32 | mockito-core 33 | 1.10.19 34 | test 35 | 36 | 37 | 38 | 39 | 40 | 41 | maven-compiler-plugin 42 | 3.1 43 | 44 | 1.8 45 | 1.8 46 | 47 | -parameters 48 | 49 | 50 | 51 | 52 | maven-surefire-plugin 53 | 2.19 54 | 55 | 56 | org.junit.platform 57 | junit-platform-surefire-provider 58 | 1.0.0-M4 59 | 60 | 61 | 62 | org.junit.jupiter 63 | junit-jupiter-engine 64 | 5.0.0-M4 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /slides/02_dynamic_tests/dynamic_tests.md: -------------------------------------------------------------------------------- 1 | slidenumbers: true 2 | 3 | # Dynamic Tests 4 | 5 | ![fit right 350%](../junit5.png) 6 | 7 | --- 8 | 9 | # Static Tests 10 | 11 | ```java 12 | class StandardTests { 13 | 14 | @Test 15 | void succeedingTest() { 16 | } 17 | 18 | @Test 19 | void failingTest() { 20 | fail("a failing test"); 21 | } 22 | 23 | @Test 24 | @Disabled("for demonstration purposes") 25 | void skippedTest() { 26 | // not executed 27 | } 28 | } 29 | ``` 30 | 31 | --- 32 | 33 | ![fit](static_tests_1.png) 34 | ![fit](static_tests_2.png) 35 | ![fit](static_tests_3.png) 36 | 37 | --- 38 | 39 | # Static Tests 40 | 41 | - fully specified at compile time 42 | - no way to programmatically add tests at runtime 43 | 44 | --- 45 | 46 | # Dynamic Tests 47 | 48 | ```java 49 | class DynamicTestsDemo { 50 | @TestFactory 51 | Stream dynamicTestsFromIntStream() { 52 | return IntStream.iterate(0, n -> n + 2).limit(10).mapToObj( 53 | n -> dynamicTest("test" + n, () -> { 54 | assertTrue(n % 2 == 0); 55 | })); 56 | } 57 | } 58 | ``` 59 | 60 | `@TestFactory` method must return a `Stream`, `Collection`, `Iterable`, or `Iterator` of `DynamicTest` instances. 61 | 62 | --- 63 | 64 | ![fit](dynamic_tests_1.png) 65 | ![fit](dynamic_tests_2.png) 66 | ![fit](dynamic_tests_3.png) 67 | 68 | --- 69 | 70 | # Why Dynamic Tests? 71 | 72 | Use plain old Java and lambdas to generate _dynamic_ tests at runtime 73 | 74 | - for a set of parameters 75 | - based on data in other files 76 | - using random data 77 | - using environment variables 78 | - depending on the operating system 79 | - etc. 80 | -------------------------------------------------------------------------------- /slides/02_dynamic_tests/dynamic_tests_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/02_dynamic_tests/dynamic_tests_1.png -------------------------------------------------------------------------------- /slides/02_dynamic_tests/dynamic_tests_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/02_dynamic_tests/dynamic_tests_2.png -------------------------------------------------------------------------------- /slides/02_dynamic_tests/dynamic_tests_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/02_dynamic_tests/dynamic_tests_3.png -------------------------------------------------------------------------------- /slides/02_dynamic_tests/static_tests_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/02_dynamic_tests/static_tests_1.png -------------------------------------------------------------------------------- /slides/02_dynamic_tests/static_tests_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/02_dynamic_tests/static_tests_2.png -------------------------------------------------------------------------------- /slides/02_dynamic_tests/static_tests_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/02_dynamic_tests/static_tests_3.png -------------------------------------------------------------------------------- /slides/05_modularization/modularization.md: -------------------------------------------------------------------------------- 1 | slidenumbers: true 2 | 3 | # Modularization 4 | 5 | ![fit right 350%](../junit5.png) 6 | 7 | --- 8 | 9 | # JUnit 4 10 | 11 | One JAR to rule them all… 12 | 13 | ![fit inline](one_jar.pdf) 14 | 15 | 16 | --- 17 | 18 | ![inline fit](usage_stats.png) 19 | 20 | ###### 21 | 22 | --- 23 | 24 | ![](modularization_1.png) 25 | 26 | --- 27 | 28 | ![](modularization_2.png) 29 | 30 | --- 31 | 32 | # [fit] Benefits of New Architecture 33 | 34 | - Clearly separated APIs for test authors and build tools/IDEs 35 | - Stable _Platform_ with an SPI for _test engines_ 36 | - _Jupiter_ API and test engine can evolve independently from _Platform_ 37 | - _Vintage_ test engine provides backward compatibility 38 | 39 | ![right 45%](modularization_1.png) 40 | 41 | --- 42 | 43 | # [fit] JUnit 5 44 | # [fit] = Platform 45 | # [fit] + Jupiter 46 | # [fit] + Vintage 47 | 48 | ![fit left 1150%](../junit5.png) 49 | 50 | --- 51 | 52 | # Compatibility 53 | 54 | ![fit right 350%](../junit5.png) 55 | 56 | --- 57 | 58 | # Backward Compatibility 59 | 60 | You can run “Vintage” JUnit 4 tests with the JUnit Platform using the JUnit Vintage Engine. 61 | 62 | ```xml 63 | 64 | maven-surefire-plugin 65 | 2.19.1 66 | 67 | 68 | 69 | org.junit.vintage 70 | junit-vintage-engine 71 | 4.12.0-M4 72 | 73 | 74 | 75 | ``` 76 | 77 | --- 78 | 79 | # Forward Compatibility 80 | 81 | The JUnit Platform provides a JUnit 4 `Runner` that can be used to run tests on the JUnit Platform. 82 | 83 | ```java 84 | import org.junit.jupiter.api.Test; 85 | 86 | @RunWith(JUnitPlatform.class) 87 | public class JupiterTest { 88 | @Test 89 | void someTest() { 90 | // test something 91 | } 92 | } 93 | ``` 94 | 95 | --- 96 | 97 | # `JUnitPlatform` in Suite Mode 98 | 99 | The `Runner` can also be used to build a test suite: 100 | 101 | ```java 102 | @RunWith(JUnitPlatform.class) 103 | @SelectPackages("com.acme") 104 | @IncludeEngines({"junit-jupiter", "junit-vintage"}) 105 | public class JUnit4SuiteDemo { 106 | // this class can be run using JUnit 4 107 | } 108 | ``` 109 | 110 | … with some limitations, e.g. no support for dynamic tests. 111 | 112 | --- 113 | 114 | # [fit] Benefits of Compatibility 115 | 116 | - Backward compatibility enables gradual migration to new API: there's no need to migrate all tests at once. 117 | - Forward compatibility provides a way to execute new tests with IDEs and build tools that don't support JUnit 5, yet. 118 | 119 | ![left](https://pixabay.com/get/e83db40f2bf51c22d9584518a33219c8b66ae3d111b4114792f5c67c/plug-185031_1920.jpg) 120 | -------------------------------------------------------------------------------- /slides/05_modularization/modularization_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/05_modularization/modularization_1.png -------------------------------------------------------------------------------- /slides/05_modularization/modularization_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/05_modularization/modularization_2.png -------------------------------------------------------------------------------- /slides/05_modularization/one_jar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/05_modularization/one_jar.pdf -------------------------------------------------------------------------------- /slides/05_modularization/usage_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/05_modularization/usage_stats.png -------------------------------------------------------------------------------- /slides/06_roadmap/roadmap.md: -------------------------------------------------------------------------------- 1 | slidenumbers: true 2 | 3 | # Roadmap 4 | 5 | ![](https://pixabay.com/get/ed30b4082efd1c22d2524518a33219c8b66ae3d111b413449df1c67e/map-455769_1280.jpg) 6 | 7 | --- 8 | 9 | # [fit] Roadmap to GA[^1] 10 | 11 | - **5.0.0-M4** (March 2017): Parameterized tests 12 | - **5.0.0-M5** (June 2017): Java 9 Compatibility 13 | - **5.0.0-RC1** (July 2017): Fixes before GA 14 | - **5.0.0** (August 2017): GA 15 | 16 | ![right](https://pixabay.com/get/ed30b4082efd1c22d2524518a33219c8b66ae3d111b413449df1c67e/map-455769_1280.jpg) 17 | 18 | [^1]: Subject to change 😉 19 | 20 | --- 21 | 22 | # Sneak Preview 23 | 24 | ![](https://pixabay.com/get/e830b40929f3063ed1584d05fb0938c9bd22ffd41db9144393f0c17dae/mountain-road-1556177_1280.jpg) 25 | 26 | --- 27 | 28 | # `@TestTemplate` (slated for M4) 29 | 30 | ```java 31 | class MyTest { 32 | @TestTemplate @ExtendWith(MyTestTemplateInvocationContextProvider.class) 33 | void testTemplate() { 34 | // test something 35 | } 36 | } 37 | 38 | class MyTestTemplateInvocationContextProvider 39 | implements TestTemplateInvocationContextProvider { 40 | @Override public Iterator provide( 41 | ContainerExtensionContext context) { 42 | return singletonIterator(new MyTestTemplateInvocationContext()); 43 | } 44 | } 45 | 46 | class MyTestTemplateInvocationContext implements TestTemplateInvocationContext { 47 | @Override public String getDisplayName(int invocationIndex) { 48 | return "[" + invocationIndex + "]"; 49 | } 50 | @Override public List getAdditionalExtensions() { 51 | return asList(new MyParameterResolver(), new MyTestInstancePostProcessor(), ...); 52 | } 53 | } 54 | ``` 55 | 56 | --- 57 | 58 | # `@ParameterizedTest` (slated for M4)[^2] 59 | 60 | ```java 61 | class ParameterizedTests { 62 | @ParameterizedTest @StringSource({ "foo, 1", "bar, 2" }) 63 | void testWithParametersFromAnnotation(String parameter, int i) { 64 | // test something 65 | } 66 | 67 | @ParameterizedTest @MethodSource("providerMethod") 68 | void testWithParametersFromMethods(String parameter) { 69 | } 70 | static Iterable providerMethod() { return asList("foo", "bar"); } 71 | 72 | @ParameterizedTest @FileSource("foo.csv") @FileSource("bar.csv") 73 | void testWithParametersFromFile(String parameter) { 74 | } 75 | } 76 | ``` 77 | 78 | [^2]: More examples: 79 | 80 | --- 81 | 82 | # `@ScenarioTest` (M5 or 5.1+)[^3] 83 | 84 | ```java 85 | @ScenarioTest 86 | class WebSecurityScenarioTest { 87 | 88 | @Step(next = "login") 89 | void visitPageRequiringAuthorizationWhileNotLoggedIn() { 90 | // attempt to visit page which requires that a user is logged in 91 | } 92 | @Step(next = "visitSecondPageRequiringAuthorizationWhileLoggedIn") 93 | void login() { 94 | // submit login form with valid credentials 95 | } 96 | @Step(next = "logout") 97 | void visitSecondPageRequiringAuthorizationWhileLoggedIn() { 98 | // visit another page which requires that a user is logged in 99 | } 100 | @Step(next = END) 101 | void logout() { 102 | // visit logout URL 103 | } 104 | } 105 | ``` 106 | 107 | [^3]: Syntax will probably change, see for details 108 | 109 | --- 110 | 111 | # [fit] Feedback wanted! 112 | 113 | Website (User Guide & Javadoc): 114 | [junit.org/junit5/](http://junit.org/junit5/) 115 | 116 | Code & Issues: 117 | [github.com/junit-team/junit5/](https://github.com/junit-team/junit5/) 118 | 119 | Twitter: 120 | [twitter.com/junitteam/](https://twitter.com/junitteam/) 121 | 122 | ![right 400%](../junit5.png) 123 | -------------------------------------------------------------------------------- /slides/junit5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junit-team/junit5-workshop/3bab5105c1128f954785cd2c9de0099b53f433c7/slides/junit5.png -------------------------------------------------------------------------------- /src/main/java/org/junit/junit5workshop/_2_dynamic_tests/PrimeNumbers.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._2_dynamic_tests; 2 | 3 | import java.util.stream.LongStream; 4 | 5 | public class PrimeNumbers { 6 | 7 | public static boolean isPrime(long n) { 8 | return LongStream.range(2, n / 2) 9 | .map(i -> n % i) 10 | .noneMatch(remainder -> remainder == 0); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/junit/junit5workshop/extensions/BenchmarkExtension.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop.extensions; 2 | 3 | class BenchmarkExtension { 4 | 5 | /* 6 | * Requirements: 7 | * - if applied to a test, it will report the run time of the individual 8 | * test without before and after behavior. 9 | * - if applied to a class, it will report the cumulated run time 10 | * of all tests therein (but not those of the individual tests) 11 | * 12 | * Tips: 13 | * - extend BeforeAllCallback, BeforeTestExecutionCallback, 14 | * AfterTestExecutionCallback, AfterAllCallback 15 | * - stateless 16 | * - use context and reflection to see whether class/method is annotated 17 | */ 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/junit/junit5workshop/extensions/DisabledOnFriday.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop.extensions; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 8 | import static java.lang.annotation.ElementType.METHOD; 9 | import static java.lang.annotation.ElementType.TYPE; 10 | 11 | @Target({ TYPE, METHOD, ANNOTATION_TYPE }) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface DisabledOnFriday { } 14 | -------------------------------------------------------------------------------- /src/main/java/org/junit/junit5workshop/extensions/DisabledOnFridayCondition.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop.extensions; 2 | 3 | class DisabledOnFridayCondition { 4 | 5 | /* 6 | * Disable tests when running on a Friday. 7 | * 8 | * Tips: 9 | * - start by extending ContainerExecutionCondition and TestExecutionCondition 10 | * - ConditionEvaluationResult has no public constructor but static factory methods 11 | */ 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/junit/junit5workshop/extensions/DisabledOnOs.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop.extensions; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 8 | import static java.lang.annotation.ElementType.METHOD; 9 | import static java.lang.annotation.ElementType.TYPE; 10 | 11 | @Target({ TYPE, METHOD, ANNOTATION_TYPE }) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface DisabledOnOs { 14 | 15 | OS[] value(); 16 | 17 | enum OS { 18 | 19 | /* 20 | * This class was written for demonstration purposes. 21 | * It is not at all fit for production! 22 | */ 23 | 24 | NIX, 25 | MAC, 26 | WINDOWS; 27 | 28 | public static OS determine() { 29 | String os = System.getProperty("os.name").toLowerCase(); 30 | 31 | if (isWindows(os)) { 32 | return WINDOWS; 33 | } else if (isMac(os)) { 34 | return MAC; 35 | } else if (isUnix(os)) { 36 | return NIX; 37 | } else { 38 | throw new IllegalArgumentException("Unknown OS \"" + os + "\"."); 39 | } 40 | } 41 | 42 | private static boolean isWindows(String os) { 43 | return os.contains("win"); 44 | } 45 | 46 | private static boolean isMac(String os) { 47 | return os.contains("mac"); 48 | } 49 | 50 | private static boolean isUnix(String os) { 51 | return os.contains("nix") || os.contains("nux"); 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/junit/junit5workshop/extensions/DisabledOnOsCondition.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop.extensions; 2 | 3 | class DisabledOnOsCondition { 4 | 5 | /* 6 | * Disable tests when running on a specific operating system. 7 | * 8 | * Tips: 9 | * - have look into @DisabledOnOs to see how to the user defines OS 10 | * and how to determine which is running 11 | * - use context and reflection to access annotation of class/method 12 | * - have a look at AnnotationUtils::findAnnotation (but don't tell Marc!) 13 | */ 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/junit/junit5workshop/extensions/SimpleBenchmarkExtension.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop.extensions; 2 | 3 | class SimpleBenchmarkExtension { 4 | 5 | /* 6 | * Write a simple benchmark that prints the runtime in ms to the console 7 | * (or publishes to the report if you feel fancy). 8 | * 9 | * Tips: 10 | * - start by extending BeforeTestExecutionCallback and AfterTestExecutionCallback 11 | * - remember, extensions should be stateless 12 | */ 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/HelloWorldTest.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.List; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | import static org.mockito.Mockito.mock; 9 | import static org.mockito.Mockito.when; 10 | 11 | /** 12 | * Typical "Hello World"; also shows that Mockito and AssertJ are compatible. 13 | */ 14 | class HelloWorldTest { 15 | 16 | @Test 17 | void helloJUnit5() { 18 | System.out.println("Hello, JUnit 5."); 19 | } 20 | 21 | @Test 22 | void usingOtherLibs() { 23 | List mockedList = when( 24 | mock(List.class) 25 | .isEmpty()) 26 | .thenReturn(true) 27 | .getMock(); 28 | // passes because we just mocked 'mockedList.isEmpty' to return true 29 | assertThat(mockedList).isEmpty(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_1_basics/_1_FirstTest.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._1_basics; 2 | 3 | class _1_FirstTest { 4 | 5 | /* 6 | * Write some tests with @Test and disable one of them with @Disabled 7 | */ 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_1_basics/_2_LifecycleTest.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._1_basics; 2 | 3 | class _2_LifecycleTest { 4 | 5 | /* 6 | * Write lifecycle methods that use the following annotations: 7 | * 8 | * - @BeforeAll, @AfterAll 9 | * - @BeforeEach, @AfterEach 10 | * 11 | * They do not have to do anything much, printing to system.out suffices. 12 | * 13 | * Write at least two tests to see how the lifecycle methods are executed. 14 | */ 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_1_basics/_3_AssertionTest.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._1_basics; 2 | 3 | class _3_AssertionTest { 4 | 5 | /* 6 | * Write tests that use a couple of basic assertions: 7 | * 8 | * - assertTrue, assertFalse (including the variants taking suppliers) 9 | * - assertNull, assertNotNull (without messages, with message, and with lazy message) 10 | * - assertEquals, assertNotEquals 11 | * - assertSame, assertNotSame 12 | * 13 | * Write tests that use the collection assertions: 14 | * 15 | * - assertArrayEquals, assertIterableEquals 16 | * 17 | * Write tests using some of the more advanced assertions: 18 | * 19 | * - assertThrows 20 | * - assertTimeout, assertTimeoutPreemptively 21 | * - assertAll 22 | * 23 | * Tests can be failed manually with `fail`. 24 | */ 25 | 26 | private boolean truism() { 27 | return true; 28 | } 29 | 30 | private void throwing() throws Exception { 31 | throw new RuntimeException("Because I can!"); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_1_basics/_4_AssumptionTest.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._1_basics; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | class _4_AssumptionTest { 6 | 7 | /* 8 | * Write tests that use assumptions to make sure 9 | * tests are only executed when preconditions are fulfilled. 10 | * 11 | * - assumeTrue 12 | * - assumeFalse 13 | * - assumeThat 14 | */ 15 | 16 | private String messageBeforeNoon() { 17 | if (LocalDateTime.now().getHour() < 12) 18 | return "Good Morning"; 19 | else 20 | throw new IllegalStateException(); 21 | } 22 | 23 | private String messageAfterNoon() { 24 | if (LocalDateTime.now().getHour() > 12) 25 | return "Good Evening"; 26 | else 27 | throw new IllegalStateException(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_1_basics/_5_NestedAndNamedTest.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._1_basics; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.DisplayName; 5 | import org.junit.jupiter.api.Nested; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.util.EmptyStackException; 9 | import java.util.Stack; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | import static org.junit.jupiter.api.Assertions.assertFalse; 13 | import static org.junit.jupiter.api.Assertions.assertThrows; 14 | import static org.junit.jupiter.api.Assertions.assertTrue; 15 | 16 | @DisplayName("A stack") 17 | class _5_NestedAndNamedTest { 18 | 19 | /* 20 | * Demonstration of JUnit 5 features (particularly nesting and naming) 21 | * that was taken from the JUnit 5 User Guide 22 | * (http://junit.org/junit5/docs/5.0.0-M4/user-guide/#writing-tests-nested) 23 | */ 24 | 25 | Stack stack; 26 | 27 | @Test 28 | @DisplayName("is instantiated with new Stack()") 29 | void isInstantiatedWithNew() { 30 | new Stack<>(); 31 | } 32 | 33 | @Nested 34 | @DisplayName("when new") 35 | class WhenNew { 36 | 37 | @BeforeEach 38 | void createNewStack() { 39 | stack = new Stack<>(); 40 | } 41 | 42 | @Test 43 | @DisplayName("is empty") 44 | void isEmpty() { 45 | assertTrue(stack.isEmpty()); 46 | } 47 | 48 | @Test 49 | @DisplayName("throws EmptyStackException when popped") 50 | void throwsExceptionWhenPopped() { 51 | assertThrows(EmptyStackException.class, () -> stack.pop()); 52 | } 53 | 54 | @Test 55 | @DisplayName("throws EmptyStackException when peeked") 56 | void throwsExceptionWhenPeeked() { 57 | assertThrows(EmptyStackException.class, () -> stack.peek()); 58 | } 59 | 60 | @Nested 61 | @DisplayName("after pushing an element") 62 | class AfterPushing { 63 | 64 | String anElement = "an element"; 65 | 66 | @BeforeEach 67 | void pushAnElement() { 68 | stack.push(anElement); 69 | } 70 | 71 | @Test 72 | @DisplayName("it is no longer empty") 73 | void isNotEmpty() { 74 | assertFalse(stack.isEmpty()); 75 | } 76 | 77 | @Test 78 | @DisplayName("returns the element when popped and is empty") 79 | void returnElementWhenPopped() { 80 | assertEquals(anElement, stack.pop()); 81 | assertTrue(stack.isEmpty()); 82 | } 83 | 84 | @Test 85 | @DisplayName("returns the element when peeked but remains not empty") 86 | void returnElementWhenPeeked() { 87 | assertEquals(anElement, stack.peek()); 88 | assertFalse(stack.isEmpty()); 89 | } 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_2_dynamic_tests/PrimeNumbersTest.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._2_dynamic_tests; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Paths; 5 | import java.util.stream.LongStream; 6 | 7 | public class PrimeNumbersTest { 8 | 9 | /* 10 | * (1) 11 | * Write dynamic tests that check {@link PrimeNumbers#isPrime(long)} 12 | * for the first 1000 prime numbers. 13 | */ 14 | 15 | /* 16 | * (2) 17 | * Write dynamic tests that check {@link PrimeNumbers#isPrime(long)} 18 | * for at least 1000 non prime numbers. 19 | */ 20 | 21 | private LongStream primes() throws Exception { 22 | return Files.lines(Paths.get(getClass().getResource("/primes-1000.txt").toURI())) 23 | .mapToLong(Long::parseLong); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_3_extensions/_1_DisabledOnFriday.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._3_extensions; 2 | 3 | class _1_DisabledOnFriday { 4 | 5 | /* 6 | * - create a condition extension that implements the contract in 7 | * src/main/java/org.junit.junit5workshop.extensions.DisabledOnFridayCondition 8 | * - apply it with @ExtendWith 9 | * - then, complete the custom annotation @DisabledOnFriday 10 | */ 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_3_extensions/_2_DisabledOnOS.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._3_extensions; 2 | 3 | class _2_DisabledOnOS { 4 | 5 | /* 6 | * Create a condition extension that implements the contract in 7 | * src/main/java/org.junit.junit5workshop.extensions.DisabledOnOsCondition 8 | */ 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_3_extensions/_3_SimpleBenchmark.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._3_extensions; 2 | 3 | class _3_SimpleBenchmark { 4 | 5 | /* 6 | * Create a benchmark extension that implements the contract in 7 | * src/main/java/org.junit.junit5workshop.extensions.SimpleBenchmarkExtension 8 | */ 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/junit/junit5workshop/_3_extensions/_4_Benchmark.java: -------------------------------------------------------------------------------- 1 | package org.junit.junit5workshop._3_extensions; 2 | 3 | class _4_Benchmark { 4 | 5 | /* 6 | * create another benchmark extension that implements the contract defined in 7 | * src/main/java/org.junit.junit5workshop.extensions.BenchmarkExtension, including another 8 | */ 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/resources/primes-1000.txt: -------------------------------------------------------------------------------- 1 | 2 2 | 3 3 | 5 4 | 7 5 | 11 6 | 13 7 | 17 8 | 19 9 | 23 10 | 29 11 | 31 12 | 37 13 | 41 14 | 43 15 | 47 16 | 53 17 | 59 18 | 61 19 | 67 20 | 71 21 | 73 22 | 79 23 | 83 24 | 89 25 | 97 26 | 101 27 | 103 28 | 107 29 | 109 30 | 113 31 | 127 32 | 131 33 | 137 34 | 139 35 | 149 36 | 151 37 | 157 38 | 163 39 | 167 40 | 173 41 | 179 42 | 181 43 | 191 44 | 193 45 | 197 46 | 199 47 | 211 48 | 223 49 | 227 50 | 229 51 | 233 52 | 239 53 | 241 54 | 251 55 | 257 56 | 263 57 | 269 58 | 271 59 | 277 60 | 281 61 | 283 62 | 293 63 | 307 64 | 311 65 | 313 66 | 317 67 | 331 68 | 337 69 | 347 70 | 349 71 | 353 72 | 359 73 | 367 74 | 373 75 | 379 76 | 383 77 | 389 78 | 397 79 | 401 80 | 409 81 | 419 82 | 421 83 | 431 84 | 433 85 | 439 86 | 443 87 | 449 88 | 457 89 | 461 90 | 463 91 | 467 92 | 479 93 | 487 94 | 491 95 | 499 96 | 503 97 | 509 98 | 521 99 | 523 100 | 541 101 | 547 102 | 557 103 | 563 104 | 569 105 | 571 106 | 577 107 | 587 108 | 593 109 | 599 110 | 601 111 | 607 112 | 613 113 | 617 114 | 619 115 | 631 116 | 641 117 | 643 118 | 647 119 | 653 120 | 659 121 | 661 122 | 673 123 | 677 124 | 683 125 | 691 126 | 701 127 | 709 128 | 719 129 | 727 130 | 733 131 | 739 132 | 743 133 | 751 134 | 757 135 | 761 136 | 769 137 | 773 138 | 787 139 | 797 140 | 809 141 | 811 142 | 821 143 | 823 144 | 827 145 | 829 146 | 839 147 | 853 148 | 857 149 | 859 150 | 863 151 | 877 152 | 881 153 | 883 154 | 887 155 | 907 156 | 911 157 | 919 158 | 929 159 | 937 160 | 941 161 | 947 162 | 953 163 | 967 164 | 971 165 | 977 166 | 983 167 | 991 168 | 997 169 | 1009 170 | 1013 171 | 1019 172 | 1021 173 | 1031 174 | 1033 175 | 1039 176 | 1049 177 | 1051 178 | 1061 179 | 1063 180 | 1069 181 | 1087 182 | 1091 183 | 1093 184 | 1097 185 | 1103 186 | 1109 187 | 1117 188 | 1123 189 | 1129 190 | 1151 191 | 1153 192 | 1163 193 | 1171 194 | 1181 195 | 1187 196 | 1193 197 | 1201 198 | 1213 199 | 1217 200 | 1223 201 | 1229 202 | 1231 203 | 1237 204 | 1249 205 | 1259 206 | 1277 207 | 1279 208 | 1283 209 | 1289 210 | 1291 211 | 1297 212 | 1301 213 | 1303 214 | 1307 215 | 1319 216 | 1321 217 | 1327 218 | 1361 219 | 1367 220 | 1373 221 | 1381 222 | 1399 223 | 1409 224 | 1423 225 | 1427 226 | 1429 227 | 1433 228 | 1439 229 | 1447 230 | 1451 231 | 1453 232 | 1459 233 | 1471 234 | 1481 235 | 1483 236 | 1487 237 | 1489 238 | 1493 239 | 1499 240 | 1511 241 | 1523 242 | 1531 243 | 1543 244 | 1549 245 | 1553 246 | 1559 247 | 1567 248 | 1571 249 | 1579 250 | 1583 251 | 1597 252 | 1601 253 | 1607 254 | 1609 255 | 1613 256 | 1619 257 | 1621 258 | 1627 259 | 1637 260 | 1657 261 | 1663 262 | 1667 263 | 1669 264 | 1693 265 | 1697 266 | 1699 267 | 1709 268 | 1721 269 | 1723 270 | 1733 271 | 1741 272 | 1747 273 | 1753 274 | 1759 275 | 1777 276 | 1783 277 | 1787 278 | 1789 279 | 1801 280 | 1811 281 | 1823 282 | 1831 283 | 1847 284 | 1861 285 | 1867 286 | 1871 287 | 1873 288 | 1877 289 | 1879 290 | 1889 291 | 1901 292 | 1907 293 | 1913 294 | 1931 295 | 1933 296 | 1949 297 | 1951 298 | 1973 299 | 1979 300 | 1987 301 | 1993 302 | 1997 303 | 1999 304 | 2003 305 | 2011 306 | 2017 307 | 2027 308 | 2029 309 | 2039 310 | 2053 311 | 2063 312 | 2069 313 | 2081 314 | 2083 315 | 2087 316 | 2089 317 | 2099 318 | 2111 319 | 2113 320 | 2129 321 | 2131 322 | 2137 323 | 2141 324 | 2143 325 | 2153 326 | 2161 327 | 2179 328 | 2203 329 | 2207 330 | 2213 331 | 2221 332 | 2237 333 | 2239 334 | 2243 335 | 2251 336 | 2267 337 | 2269 338 | 2273 339 | 2281 340 | 2287 341 | 2293 342 | 2297 343 | 2309 344 | 2311 345 | 2333 346 | 2339 347 | 2341 348 | 2347 349 | 2351 350 | 2357 351 | 2371 352 | 2377 353 | 2381 354 | 2383 355 | 2389 356 | 2393 357 | 2399 358 | 2411 359 | 2417 360 | 2423 361 | 2437 362 | 2441 363 | 2447 364 | 2459 365 | 2467 366 | 2473 367 | 2477 368 | 2503 369 | 2521 370 | 2531 371 | 2539 372 | 2543 373 | 2549 374 | 2551 375 | 2557 376 | 2579 377 | 2591 378 | 2593 379 | 2609 380 | 2617 381 | 2621 382 | 2633 383 | 2647 384 | 2657 385 | 2659 386 | 2663 387 | 2671 388 | 2677 389 | 2683 390 | 2687 391 | 2689 392 | 2693 393 | 2699 394 | 2707 395 | 2711 396 | 2713 397 | 2719 398 | 2729 399 | 2731 400 | 2741 401 | 2749 402 | 2753 403 | 2767 404 | 2777 405 | 2789 406 | 2791 407 | 2797 408 | 2801 409 | 2803 410 | 2819 411 | 2833 412 | 2837 413 | 2843 414 | 2851 415 | 2857 416 | 2861 417 | 2879 418 | 2887 419 | 2897 420 | 2903 421 | 2909 422 | 2917 423 | 2927 424 | 2939 425 | 2953 426 | 2957 427 | 2963 428 | 2969 429 | 2971 430 | 2999 431 | 3001 432 | 3011 433 | 3019 434 | 3023 435 | 3037 436 | 3041 437 | 3049 438 | 3061 439 | 3067 440 | 3079 441 | 3083 442 | 3089 443 | 3109 444 | 3119 445 | 3121 446 | 3137 447 | 3163 448 | 3167 449 | 3169 450 | 3181 451 | 3187 452 | 3191 453 | 3203 454 | 3209 455 | 3217 456 | 3221 457 | 3229 458 | 3251 459 | 3253 460 | 3257 461 | 3259 462 | 3271 463 | 3299 464 | 3301 465 | 3307 466 | 3313 467 | 3319 468 | 3323 469 | 3329 470 | 3331 471 | 3343 472 | 3347 473 | 3359 474 | 3361 475 | 3371 476 | 3373 477 | 3389 478 | 3391 479 | 3407 480 | 3413 481 | 3433 482 | 3449 483 | 3457 484 | 3461 485 | 3463 486 | 3467 487 | 3469 488 | 3491 489 | 3499 490 | 3511 491 | 3517 492 | 3527 493 | 3529 494 | 3533 495 | 3539 496 | 3541 497 | 3547 498 | 3557 499 | 3559 500 | 3571 501 | 3581 502 | 3583 503 | 3593 504 | 3607 505 | 3613 506 | 3617 507 | 3623 508 | 3631 509 | 3637 510 | 3643 511 | 3659 512 | 3671 513 | 3673 514 | 3677 515 | 3691 516 | 3697 517 | 3701 518 | 3709 519 | 3719 520 | 3727 521 | 3733 522 | 3739 523 | 3761 524 | 3767 525 | 3769 526 | 3779 527 | 3793 528 | 3797 529 | 3803 530 | 3821 531 | 3823 532 | 3833 533 | 3847 534 | 3851 535 | 3853 536 | 3863 537 | 3877 538 | 3881 539 | 3889 540 | 3907 541 | 3911 542 | 3917 543 | 3919 544 | 3923 545 | 3929 546 | 3931 547 | 3943 548 | 3947 549 | 3967 550 | 3989 551 | 4001 552 | 4003 553 | 4007 554 | 4013 555 | 4019 556 | 4021 557 | 4027 558 | 4049 559 | 4051 560 | 4057 561 | 4073 562 | 4079 563 | 4091 564 | 4093 565 | 4099 566 | 4111 567 | 4127 568 | 4129 569 | 4133 570 | 4139 571 | 4153 572 | 4157 573 | 4159 574 | 4177 575 | 4201 576 | 4211 577 | 4217 578 | 4219 579 | 4229 580 | 4231 581 | 4241 582 | 4243 583 | 4253 584 | 4259 585 | 4261 586 | 4271 587 | 4273 588 | 4283 589 | 4289 590 | 4297 591 | 4327 592 | 4337 593 | 4339 594 | 4349 595 | 4357 596 | 4363 597 | 4373 598 | 4391 599 | 4397 600 | 4409 601 | 4421 602 | 4423 603 | 4441 604 | 4447 605 | 4451 606 | 4457 607 | 4463 608 | 4481 609 | 4483 610 | 4493 611 | 4507 612 | 4513 613 | 4517 614 | 4519 615 | 4523 616 | 4547 617 | 4549 618 | 4561 619 | 4567 620 | 4583 621 | 4591 622 | 4597 623 | 4603 624 | 4621 625 | 4637 626 | 4639 627 | 4643 628 | 4649 629 | 4651 630 | 4657 631 | 4663 632 | 4673 633 | 4679 634 | 4691 635 | 4703 636 | 4721 637 | 4723 638 | 4729 639 | 4733 640 | 4751 641 | 4759 642 | 4783 643 | 4787 644 | 4789 645 | 4793 646 | 4799 647 | 4801 648 | 4813 649 | 4817 650 | 4831 651 | 4861 652 | 4871 653 | 4877 654 | 4889 655 | 4903 656 | 4909 657 | 4919 658 | 4931 659 | 4933 660 | 4937 661 | 4943 662 | 4951 663 | 4957 664 | 4967 665 | 4969 666 | 4973 667 | 4987 668 | 4993 669 | 4999 670 | 5003 671 | 5009 672 | 5011 673 | 5021 674 | 5023 675 | 5039 676 | 5051 677 | 5059 678 | 5077 679 | 5081 680 | 5087 681 | 5099 682 | 5101 683 | 5107 684 | 5113 685 | 5119 686 | 5147 687 | 5153 688 | 5167 689 | 5171 690 | 5179 691 | 5189 692 | 5197 693 | 5209 694 | 5227 695 | 5231 696 | 5233 697 | 5237 698 | 5261 699 | 5273 700 | 5279 701 | 5281 702 | 5297 703 | 5303 704 | 5309 705 | 5323 706 | 5333 707 | 5347 708 | 5351 709 | 5381 710 | 5387 711 | 5393 712 | 5399 713 | 5407 714 | 5413 715 | 5417 716 | 5419 717 | 5431 718 | 5437 719 | 5441 720 | 5443 721 | 5449 722 | 5471 723 | 5477 724 | 5479 725 | 5483 726 | 5501 727 | 5503 728 | 5507 729 | 5519 730 | 5521 731 | 5527 732 | 5531 733 | 5557 734 | 5563 735 | 5569 736 | 5573 737 | 5581 738 | 5591 739 | 5623 740 | 5639 741 | 5641 742 | 5647 743 | 5651 744 | 5653 745 | 5657 746 | 5659 747 | 5669 748 | 5683 749 | 5689 750 | 5693 751 | 5701 752 | 5711 753 | 5717 754 | 5737 755 | 5741 756 | 5743 757 | 5749 758 | 5779 759 | 5783 760 | 5791 761 | 5801 762 | 5807 763 | 5813 764 | 5821 765 | 5827 766 | 5839 767 | 5843 768 | 5849 769 | 5851 770 | 5857 771 | 5861 772 | 5867 773 | 5869 774 | 5879 775 | 5881 776 | 5897 777 | 5903 778 | 5923 779 | 5927 780 | 5939 781 | 5953 782 | 5981 783 | 5987 784 | 6007 785 | 6011 786 | 6029 787 | 6037 788 | 6043 789 | 6047 790 | 6053 791 | 6067 792 | 6073 793 | 6079 794 | 6089 795 | 6091 796 | 6101 797 | 6113 798 | 6121 799 | 6131 800 | 6133 801 | 6143 802 | 6151 803 | 6163 804 | 6173 805 | 6197 806 | 6199 807 | 6203 808 | 6211 809 | 6217 810 | 6221 811 | 6229 812 | 6247 813 | 6257 814 | 6263 815 | 6269 816 | 6271 817 | 6277 818 | 6287 819 | 6299 820 | 6301 821 | 6311 822 | 6317 823 | 6323 824 | 6329 825 | 6337 826 | 6343 827 | 6353 828 | 6359 829 | 6361 830 | 6367 831 | 6373 832 | 6379 833 | 6389 834 | 6397 835 | 6421 836 | 6427 837 | 6449 838 | 6451 839 | 6469 840 | 6473 841 | 6481 842 | 6491 843 | 6521 844 | 6529 845 | 6547 846 | 6551 847 | 6553 848 | 6563 849 | 6569 850 | 6571 851 | 6577 852 | 6581 853 | 6599 854 | 6607 855 | 6619 856 | 6637 857 | 6653 858 | 6659 859 | 6661 860 | 6673 861 | 6679 862 | 6689 863 | 6691 864 | 6701 865 | 6703 866 | 6709 867 | 6719 868 | 6733 869 | 6737 870 | 6761 871 | 6763 872 | 6779 873 | 6781 874 | 6791 875 | 6793 876 | 6803 877 | 6823 878 | 6827 879 | 6829 880 | 6833 881 | 6841 882 | 6857 883 | 6863 884 | 6869 885 | 6871 886 | 6883 887 | 6899 888 | 6907 889 | 6911 890 | 6917 891 | 6947 892 | 6949 893 | 6959 894 | 6961 895 | 6967 896 | 6971 897 | 6977 898 | 6983 899 | 6991 900 | 6997 901 | 7001 902 | 7013 903 | 7019 904 | 7027 905 | 7039 906 | 7043 907 | 7057 908 | 7069 909 | 7079 910 | 7103 911 | 7109 912 | 7121 913 | 7127 914 | 7129 915 | 7151 916 | 7159 917 | 7177 918 | 7187 919 | 7193 920 | 7207 921 | 7211 922 | 7213 923 | 7219 924 | 7229 925 | 7237 926 | 7243 927 | 7247 928 | 7253 929 | 7283 930 | 7297 931 | 7307 932 | 7309 933 | 7321 934 | 7331 935 | 7333 936 | 7349 937 | 7351 938 | 7369 939 | 7393 940 | 7411 941 | 7417 942 | 7433 943 | 7451 944 | 7457 945 | 7459 946 | 7477 947 | 7481 948 | 7487 949 | 7489 950 | 7499 951 | 7507 952 | 7517 953 | 7523 954 | 7529 955 | 7537 956 | 7541 957 | 7547 958 | 7549 959 | 7559 960 | 7561 961 | 7573 962 | 7577 963 | 7583 964 | 7589 965 | 7591 966 | 7603 967 | 7607 968 | 7621 969 | 7639 970 | 7643 971 | 7649 972 | 7669 973 | 7673 974 | 7681 975 | 7687 976 | 7691 977 | 7699 978 | 7703 979 | 7717 980 | 7723 981 | 7727 982 | 7741 983 | 7753 984 | 7757 985 | 7759 986 | 7789 987 | 7793 988 | 7817 989 | 7823 990 | 7829 991 | 7841 992 | 7853 993 | 7867 994 | 7873 995 | 7877 996 | 7879 997 | 7883 998 | 7901 999 | 7907 1000 | 7919 1001 | --------------------------------------------------------------------------------