├── .gitignore ├── 00-junit-best-practice ├── build.gradle └── src │ └── test │ └── java │ └── com.dmitrijdrandarov │ └── bestpractice │ └── JUnit_BestPractice.java ├── 01-junit-5 ├── build.gradle └── src │ └── test │ ├── java │ └── com.dmitrijdrandarov │ │ └── junit5 │ │ ├── JUnit5_00_Mixing_JUnit4_JUnit5.java │ │ ├── JUnit5_01_GeneralChanges.java │ │ ├── JUnit5_02_NewFeaturesBasics.java │ │ ├── JUnit5_03_NewFeaturesAdvanced.java │ │ ├── JUnit5_04_AdvancedExamples.java │ │ └── utils │ │ ├── benchmarked │ │ ├── BenchmarkExtension.java │ │ └── Benchmarked.java │ │ ├── parameterresolver │ │ ├── ClassName_ParameterResolver.java │ │ └── ParameterIndex_ParameterResolver.java │ │ ├── simpleextension │ │ ├── DisabledOnMonday.java │ │ ├── DisabledOnWeekday.java │ │ └── DisabledWeekdays.java │ │ └── testannotationextension │ │ ├── PrintUITestData.java │ │ ├── RootElementResolver.java │ │ └── UITest.java │ └── resources │ └── fxml │ └── sample.fxml ├── 02-assertj ├── build.gradle └── src │ └── test │ └── java │ └── com.dmitrijdrandarov │ └── assertj │ ├── AssertJ_01_Comparison.java │ └── AssertJ_02_FluentAssertionsAndMoreFeatures.java ├── 03-spring ├── 03-01-spring-boot-4 │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── dmitrijdrandarov │ │ │ └── SpringApplication.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── dmitrijdrandarov │ │ │ ├── junit4 │ │ │ └── SpringBoot4_01_JUnit4Test.java │ │ │ ├── junit5 │ │ │ └── SpringBoot4_02_JUnit5Test.java │ │ │ └── repositories │ │ │ └── DummyFruitRepository.java │ │ └── resources │ │ ├── application.properties │ │ ├── data.sql │ │ └── schema.sql ├── 03-02-spring-boot-5 │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── dmitrijdrandarov │ │ │ └── SpringApplication.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── dmitrijdrandarov │ │ │ ├── junit4 │ │ │ └── SpringBoot5_01_JUnit4Test.java │ │ │ ├── junit5 │ │ │ └── SpringBoot5_02_JUnit5Test.java │ │ │ └── repositories │ │ │ └── DummyFruitRepository.java │ │ └── resources │ │ ├── application.properties │ │ ├── data.sql │ │ └── schema.sql ├── 03-03-spring-4 │ ├── build.gradle │ └── src │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── dmitrijdrandarov │ │ │ └── spring4 │ │ │ ├── Spring4Config.java │ │ │ ├── junit4 │ │ │ └── Spring4_01_JUnit4Test.java │ │ │ ├── junit5 │ │ │ └── Spring4_02_JUnit5Test.java │ │ │ └── repositories │ │ │ └── DummyFruitRepository.java │ │ └── resources │ │ ├── data.sql │ │ └── schema.sql └── 03-04-spring-5 │ ├── build.gradle │ └── src │ └── test │ ├── java │ └── com │ │ └── dmitrijdrandarov │ │ └── spring5 │ │ ├── Spring5Config.java │ │ ├── junit4 │ │ └── Spring5_01_JUnit4Test.java │ │ ├── junit5 │ │ └── Spring5_02_JUnit5Test.java │ │ └── repositories │ │ └── DummyFruitRepository.java │ └── resources │ ├── data.sql │ └── schema.sql ├── 04-testfx └── build.gradle ├── 05-mockito ├── build.gradle └── src │ └── test │ └── java │ └── com.dmitrijdrandarov │ └── mockito │ └── Mockito_Basics.java ├── JUG - Modern Testing mit JUnit 5 - Light.pptx ├── JUG - Modern Testing mit JUnit 5.pptx ├── LICENSE ├── README.adoc ├── _config.yml ├── build.gradle ├── common ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── dmitrijdrandarov │ ├── entities │ ├── DummyFruit.java │ └── DummyObject.java │ └── utils │ ├── DummyFruitFactory.java │ └── DummyUtil.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── 00_architecture.png ├── 01_displayname_result.png ├── 02_nestedTests_result.png ├── 03_testFactory_result.png ├── 04_benchmarked_output.png ├── 05_jug_final_small.png ├── github.png ├── linkedin.png ├── twitter.png └── xing.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | out/ 3 | target/ 4 | build/ 5 | 6 | *.jar 7 | *.iml 8 | 9 | .gradle 10 | codemr/ -------------------------------------------------------------------------------- /00-junit-best-practice/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: '1.4.0' 5 | 6 | 7 | // Commons 8 | testCompile project(":common") 9 | 10 | 11 | // Logging 12 | testCompile "org.slf4j:slf4j-simple:$slf4j_version" 13 | } 14 | 15 | // Execute Tests from Jar with 16 | // java -jar junit-platform-console-standalone-1.4.0.jar -cp (abolute)\buidl\libs\00-junit-best-practice-tests.jar --scan-classpath --include-classname="^(.)*$" 17 | task testJar(type: Jar) { 18 | getArchiveClassifier().set("tests") 19 | 20 | from { 21 | configurations.testCompile.collect { it.isDirectory() ? it : zipTree(it) } 22 | } 23 | 24 | from { 25 | sourceSets.test.output 26 | } 27 | } -------------------------------------------------------------------------------- /00-junit-best-practice/src/test/java/com.dmitrijdrandarov/bestpractice/JUnit_BestPractice.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.bestpractice; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import com.dmitrijdrandarov.entities.DummyFruit.TYPE; 5 | import com.dmitrijdrandarov.utils.DummyUtil; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.opentest4j.AssertionFailedError; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | import static org.junit.jupiter.api.Assertions.*; 17 | 18 | /** 19 | * A demo-class with a collection of simple best-practices when writing JUnit-Tests. 20 | * 21 | * @author dmitrij-drandarov 22 | * @since 10 Feb 2017 23 | */ 24 | class JUnit_BestPractice { 25 | 26 | private static final Logger LOG = LoggerFactory.getLogger(JUnit_BestPractice.class); 27 | 28 | private Map dummyFruits; 29 | 30 | @BeforeEach 31 | void dummy() { 32 | dummyFruits = new HashMap<>(); 33 | dummyFruits.put(1, new DummyFruit(1L, TYPE.BANANA, "Baby Banana", "It's yellow!", 20.0)); 34 | dummyFruits.put(2, new DummyFruit(2L, TYPE.APPLE, "Granny Smith Apple", "Delicious!", 10.5)); 35 | dummyFruits.put(3, new DummyFruit(3L, TYPE.ORANGE, "Grapefruit", "It's totally an orange, baka!", 8.5)); 36 | } 37 | 38 | 39 | //------------------------------------------------------------------------------------------------------------------------ 40 | // Choosing the right assertion-type 41 | //------------------------------------------------------------------------------------------------------------------------ 42 | 43 | /** 44 | * Don't check for nulls with {@link Assertions#assertTrue}. Error messages will be meaningless and therefore useless. 45 | * Do use {@link Assertions#assertNotNull} or {@link Assertions#assertNull} instead. 46 | */ 47 | @Test 48 | void assertTrueNotNullTest() { 49 | // Unclear, useless error-message when tests fail 50 | AssertionFailedError ex = assertThrows(AssertionFailedError.class, 51 | () -> assertTrue(dummyFruits.get(4) != null)); 52 | 53 | LOG.info(ex.toString()); 54 | // org.opentest4j.AssertionFailedError // It couldn't be more useless... 55 | } 56 | 57 | @Test 58 | void assertNotNullTest() { 59 | // Better error-message you can actually use and understand 60 | AssertionFailedError ex = assertThrows(AssertionFailedError.class, 61 | () -> assertNotNull(dummyFruits.get(4))); 62 | 63 | LOG.info(ex.toString()); 64 | // org.opentest4j.AssertionFailedError: expected: not 65 | } 66 | 67 | /** 68 | * The same applies for using {@link Assertions#assertTrue} and {@link Object#equals}. Use {@link Assertions#assertEquals} instead. 69 | */ 70 | @Test 71 | void assertTrueEqualsTest() { 72 | // Unclear, useless error-message when tests fail 73 | AssertionFailedError ex = assertThrows(AssertionFailedError.class, 74 | () -> assertTrue(TYPE.BANANA.equals(dummyFruits.get(2).getType()))); 75 | 76 | LOG.info(ex.toString()); 77 | // org.opentest4j.AssertionFailedError: // It - again - couldn't be more useless... 78 | } 79 | 80 | @Test 81 | void assertEqualsTest() { 82 | // Really useful error-message you can actually use and understand 83 | AssertionFailedError ex = assertThrows(AssertionFailedError.class, 84 | () -> 85 | assertEquals(TYPE.BANANA, dummyFruits.get(2).getType())); 86 | 87 | LOG.info(ex.toString()); 88 | // org.opentest4j.AssertionFailedError: expected: but was: 89 | } 90 | 91 | 92 | //------------------------------------------------------------------------------------------------------------------------ 93 | // Working with delta 94 | //------------------------------------------------------------------------------------------------------------------------ 95 | 96 | /** 97 | * It may seem trivial, but they happen more often than you would assume. 98 | */ 99 | @Test 100 | void wrongDelta() { 101 | assertNotEquals(0.9, DummyUtil.calculateTimesThree(0.3)); // --> That's why the delta is important 102 | 103 | // Using a 0.0 delta doesn't make much sense and JUnit 5 actively prevents it 104 | AssertionFailedError ex = assertThrows(AssertionFailedError.class, 105 | () -> assertEquals(20.9, DummyUtil.calculateTimesThree(19), 0.0)); 106 | 107 | LOG.info(ex.toString()); 108 | // org.opentest4j.AssertionFailedError: positive delta expected but was: <0.0> 109 | 110 | 111 | // With such a high delta the correctness of the calculation is no longer assured 112 | assertEquals(15.5, DummyUtil.calculateTimesThree(5.0), 0.5); 113 | } 114 | 115 | /** 116 | * You should use a very small delta, since the variations caused by using doubles are mostly as small as e.g. 0.0000000000004. 117 | */ 118 | @Test 119 | void betterDelta() { 120 | assertEquals(15.0, DummyUtil.calculateTimesThree(5.0), 0.0000000001); 121 | } 122 | 123 | /** 124 | * Starting with JUnit 5 you no longer have to necessarily declare the delta, since it will be checked by the framework. 125 | * For implementation details look in {@link org.junit.jupiter.api.AssertionUtils#doublesAreEqual(double, double)}. 126 | * 127 | * If you use the delta for rounding mistakes, really think about it: You 'actually' calculate rounded values, but you 'expect' not rounded ones, 128 | * so you just use a delta as a workaround? Really?! That's not how you check for 'equality'! 129 | */ 130 | @Test 131 | void idealDelta() { 132 | assertEquals(15.0, DummyUtil.calculateTimesThree(5.0)); 133 | } 134 | 135 | 136 | //------------------------------------------------------------------------------------------------------------------------ 137 | // Correct parameter order 138 | //------------------------------------------------------------------------------------------------------------------------ 139 | 140 | @Test 141 | void wrongParameterOrder() { 142 | //Gives you unclear, actually wrong fail-reports! 143 | AssertionFailedError ex = assertThrows(AssertionFailedError.class, 144 | () -> assertEquals(dummyFruits.get(2).getName(), "Grapefruit")); 145 | // . . . . . . . . . . . . . . ^expected . . . . . . . . . . . ^actual 146 | 147 | LOG.info(ex.toString()); 148 | // org.opentest4j.AssertionFailedError: expected: but was: 149 | 150 | // So wait, the constant String I put in there is not an expected value? Tell me more about that! 151 | // Or don't... Just fix the order and everything will be fine ;) 152 | } 153 | 154 | @Test 155 | void correctParameterOrder() { 156 | // Clear and useful fail-report ;) 157 | AssertionFailedError ex = assertThrows(AssertionFailedError.class, 158 | () -> assertEquals("Grapefruit", dummyFruits.get(2).getName())); 159 | // . . . . . . . . . . . . . . ^expected . . . . . . ^actual 160 | 161 | LOG.info(ex.toString()); 162 | // org.opentest4j.AssertionFailedError: expected: but was: 163 | 164 | // "Oh... my actual value is not what was expected? Well now I know what the problem is and I can fix it!" 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /01-junit-5/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | 5 | 6 | // Lagacy support for JUnit 4 7 | testCompile "org.junit.vintage:junit-vintage-engine:$junit5_vintage4_version" 8 | testCompile "junit:junit:$junit4_version" 9 | 10 | 11 | // Commons 12 | testCompile project(":common") 13 | 14 | 15 | // Logging 16 | testCompile "org.slf4j:slf4j-simple:$slf4j_version" 17 | 18 | 19 | // JavaFX (pretty broken right now with JDK 11) 20 | compile "org.openjfx:javafx.base:$openjfx_version" 21 | compile "org.openjfx:javafx.graphics:$openjfx_version" 22 | compile "org.openjfx:javafx.controls:$openjfx_version" 23 | compile "org.openjfx:javafx.fxml:$openjfx_version:mac" 24 | 25 | } -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_00_Mixing_JUnit4_JUnit5.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5; 2 | 3 | import org.junit.Assert; 4 | import org.junit.jupiter.api.Assertions; 5 | 6 | /** 7 | * This all happens within the JUnit 5 environment! Therefore you can make the class package-private. 8 | * 9 | * JUnit 5 is managed by: 10 | * org.junit.jupiter:junit-jupiter-api:5.*.* 11 | * 12 | * JUnit 4 lagacy support is managed by: 13 | * org.junit.vintage:junit-vintage-engine:4.12.0 14 | * 15 | * Public access modifier is still required on mixed test classes. 16 | */ 17 | public class JUnit5_00_Mixing_JUnit4_JUnit5 { 18 | 19 | /** 20 | * Public access modifier is still required 21 | */ 22 | @org.junit.Test 23 | public void junit4Test() { 24 | Assert.assertTrue(true); // JUnit 4 Assertion 25 | Assertions.assertTrue(true); // JUnit 5 Assertion 26 | } 27 | 28 | @org.junit.jupiter.api.Test 29 | void junit5Test() { 30 | Assert.assertTrue(true); // JUnit 4 Assertion 31 | Assertions.assertTrue(true); // JUnit 5 Assertion 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_01_GeneralChanges.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5; 2 | 3 | import org.junit.jupiter.api.*; 4 | import org.junit.jupiter.api.extension.ExecutionCondition; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | import static org.junit.jupiter.api.Assumptions.assumeTrue; 8 | 9 | /** 10 | * Contains a collection of small or general changes made to the API. 11 | * 12 | * @author dmitrij-drandarov 13 | * @since 21 Jul 2016 14 | */ 15 | class JUnit5_01_GeneralChanges { 16 | 17 | //------------------------------------------------------------------------------------------------------------------------ 18 | // Test-Methods 19 | //------------------------------------------------------------------------------------------------------------------------ 20 | 21 | /** 22 | * Tests now only must not be static or private. Latter also goes for @Before.../@After... 23 | * timeout = ? and expected = ? functionality has now moved elsewhere. See in {@link JUnit5_02_NewFeaturesBasics} 24 | */ 25 | @Test 26 | void testTest() {} 27 | 28 | 29 | //------------------------------------------------------------------------------------------------------------------------ 30 | // Annotations 31 | //------------------------------------------------------------------------------------------------------------------------ 32 | 33 | /** 34 | * Annotation @BeforeClass was replaced by @{@link BeforeAll}. Needs to be static. Same for @AfterClass. 35 | */ 36 | @BeforeAll 37 | static void beforeAll() {} 38 | 39 | /** 40 | * Annotation @Before was replaced by @{@link BeforeEach}. Same for @After. 41 | */ 42 | @BeforeEach 43 | void beforeEach() {} 44 | 45 | /** 46 | * Annotation @Ignore was replaced by @{@link Disabled}. Sounds less negative. 47 | * However a reason for the deactivation will be printed which can be more advanced with features like {@link ExecutionCondition}. 48 | */ 49 | //@Disabled 50 | @Test 51 | void disabledTest() {} 52 | 53 | /** 54 | * JUnit 4s experimental @Category is now called {@link Tag}/{@link Tags}. 55 | */ 56 | @Tag("abc") 57 | @Test 58 | void taggedTest() {} 59 | 60 | 61 | //------------------------------------------------------------------------------------------------------------------------ 62 | // Assertions / Assumptions 63 | //------------------------------------------------------------------------------------------------------------------------ 64 | 65 | /** 66 | * Assertion Methods are now in class {@link Assertions}. Method names stayed mostly the same otherwise. 67 | */ 68 | @Test 69 | void assertionsTest() { 70 | Assertions.assertTrue(true); // Without static import 71 | assertTrue(true); // With static import on org.junit.jupiter.api.Assertions.assertTrue() 72 | } 73 | 74 | /** 75 | * Assumption Methods are now in class {@link Assumptions}. Method names stayed mostly the same otherwise. 76 | */ 77 | @Test 78 | void assumptionsTest() { 79 | Assumptions.assumeTrue(true); // Without static import 80 | assumeTrue(true); // With static import on org.junit.jupiter.api.Assumptions.assumeTrue() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_02_NewFeaturesBasics.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5; 2 | 3 | import org.junit.jupiter.api.*; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.junit.jupiter.api.extension.Extension; 6 | import org.junit.jupiter.api.extension.ParameterResolver; 7 | import org.junit.jupiter.api.function.Executable; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.util.function.BooleanSupplier; 12 | import java.util.function.Supplier; 13 | 14 | import static org.junit.jupiter.api.Assertions.*; 15 | 16 | /** 17 | * Contains a collection of smaller, general new features like lambda support or small annotations. 18 | * 19 | * @author dmitrij-drandarov 20 | * @since 22 Jul 2016 21 | */ 22 | class JUnit5_02_NewFeaturesBasics { 23 | 24 | private static final Logger LOG = LoggerFactory.getLogger(JUnit5_02_NewFeaturesBasics.class); 25 | 26 | 27 | //------------------------------------------------------------------------------------------------------------------------ 28 | // Test-Methods 29 | //------------------------------------------------------------------------------------------------------------------------ 30 | 31 | /** 32 | * Tests can now receive Display-Names via @{@link DisplayName}. 33 | * These are e.g. used by the IDE, Console or the {@link TestInfo}-Parameter (addressed in {@link #parameterTest(TestInfo, TestReporter)}). 34 | */ 35 | @Test 36 | @DisplayName("Choose a display name") 37 | void displayNameTest() {} 38 | 39 | @Nested 40 | @DisplayName("Tests grouped by something") 41 | class groupedTests { 42 | 43 | @Test 44 | void groupedTest1() {} 45 | 46 | @Test 47 | void groupedTest2() {} 48 | 49 | } 50 | 51 | 52 | //------------------------------------------------------------------------------------------------------------------------ 53 | // Assertions / Assumptions 54 | //------------------------------------------------------------------------------------------------------------------------ 55 | 56 | /** 57 | * JUnit 5 includes several new Functional-Interface. One of the most important one is called {@link Executable}. 58 | * It is used for certain assertions such as {@link Assertions#assertAll(Executable...)} and {@link Assertions#assertThrows(Class, Executable)}. 59 | */ 60 | @Test 61 | void executablesTest() { 62 | Executable ex = () -> assertTrue(5 <= 10); 63 | Executable ex2 = () -> assertFalse(Boolean.FALSE); 64 | assertAll(ex, ex2); 65 | } 66 | 67 | /** 68 | * The new assertion-methods now support supplier-interfaces, meaning you can now enter lambda expressions on the fly to a lot of the assert-methods. 69 | * E.g. by giving a {@link BooleanSupplier} for the assertion and a {@link Supplier} for the result-message 70 | * to the {@link Assertions#assertTrue(BooleanSupplier, Supplier)} method. 71 | */ 72 | @Test 73 | void assertLambdaTest() { 74 | assertTrue(() -> Boolean.parseBoolean("true")); // Simple assertTrue() with BooleanSupplier-Lambda-Implement. 75 | Assertions.assertTrue(true, this.getClass()::getName); // Method references are possible as well of course 76 | } 77 | 78 | /** 79 | * {@link Assertions} has a method called {@link Assertions#assertAll(Executable...)} that enables us to group assertions, as well as reuse them. 80 | */ 81 | @Test 82 | void assertAllTest() { 83 | Executable[] executables = { 84 | () -> assertTrue(getData() >= -10), 85 | () -> assertTrue(getData() <= +15)}; 86 | 87 | Assertions.assertAll("Random Tests", executables); 88 | dataChanges(); 89 | Assertions.assertAll("Random Tests Again", executables); 90 | } 91 | 92 | /** 93 | * The expected parameter of {@link Test} has moved to {@link Assertions#assertThrows(Class, Executable)}. 94 | */ 95 | @Test 96 | void assertThrowsTest() { 97 | assertThrows( 98 | ArrayIndexOutOfBoundsException.class, 99 | () -> (new String[1])[2] = "I will throw an Exception :)"); 100 | } 101 | 102 | /** 103 | * You can also use {@link Assertions#assertThrows(Class, Executable)} to get the {@link Exception}-Instance if you need it. 104 | */ 105 | @Test 106 | void expectThrowsTest() { 107 | ArrayIndexOutOfBoundsException exc = assertThrows( 108 | ArrayIndexOutOfBoundsException.class, 109 | () -> (new String[1])[2] = "I will throw an Exception :)"); 110 | 111 | assertEquals(exc.getMessage(), "Index 2 out of bounds for length 1"); 112 | } 113 | 114 | private int i = 10; 115 | private void dataChanges() { i++; } 116 | private int getData() { return i; } 117 | 118 | /** 119 | * {@link Assumptions} of course support Suppliers as well. 120 | */ 121 | @Test 122 | void assumptionsLambdaTest() { 123 | Assumptions.assumeTrue(() -> Boolean.parseBoolean("true")); 124 | } 125 | 126 | 127 | //------------------------------------------------------------------------------------------------------------------------ 128 | // Test-Parameters 129 | //------------------------------------------------------------------------------------------------------------------------ 130 | 131 | /** 132 | * Tests can now be provided with parameters. 133 | * Those are resolved by {@link ParameterResolver}-Implementations which in turn are extensions of the above mentioned {@link Extension}. 134 | * This enables dependency injection at method level. 135 | * 136 | * Resolvers for {@link TestInfo} and {@link TestReporter} are already provided. 137 | * Other parameters require your own {@link ParameterResolver}-Implementations to be added 138 | * with the @{@link ExtendWith}-Annotation to either the class or method. 139 | * 140 | * @param testInfo Information about the current test 141 | * @param testReporter Used to publish test entries 142 | */ 143 | @Test 144 | void parameterTest(TestInfo testInfo, TestReporter testReporter) { 145 | testReporter.publishEntry("parameterTestTime", String.valueOf(System.currentTimeMillis())); 146 | 147 | LOG.info("DisplayName:\t" + testInfo.getDisplayName()); 148 | LOG.info("Tags:\t\t\t" + testInfo.getTags()); 149 | LOG.info("TestClass:\t\t" + testInfo.getTestClass()); 150 | LOG.info("TestMethod:\t\t" + testInfo.getTestMethod()); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_03_NewFeaturesAdvanced.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import com.dmitrijdrandarov.entities.DummyFruit.TYPE; 5 | import com.dmitrijdrandarov.junit5.utils.parameterresolver.ClassName_ParameterResolver; 6 | import com.dmitrijdrandarov.junit5.utils.parameterresolver.ParameterIndex_ParameterResolver; 7 | import com.dmitrijdrandarov.junit5.utils.simpleextension.DisabledOnMonday; 8 | import org.junit.jupiter.api.*; 9 | import org.junit.jupiter.api.extension.*; 10 | import org.junit.jupiter.api.function.ThrowingConsumer; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.*; 15 | import java.util.function.Function; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | /** 20 | * A collection of new, advanced or experimental features introduced in JUnit 5. 21 | * 22 | * @author dmitrij-drandarov 23 | * @since 22 Jul 2016 24 | */ 25 | class JUnit5_03_NewFeaturesAdvanced { 26 | 27 | private Map dummyFruits; 28 | 29 | @BeforeEach 30 | void dummy() { 31 | dummyFruits = new HashMap<>(); 32 | dummyFruits.put(1, new DummyFruit(1L, TYPE.BANANA, "Baby Banana", "It's yellow!", 20.0)); 33 | dummyFruits.put(2, new DummyFruit(2L, TYPE.APPLE, "Granny Smith Apple", "Delicious!", 10.5)); 34 | dummyFruits.put(3, new DummyFruit(3L, TYPE.ORANGE, "Grapefruit", "It's totally an orange, baka!", 8.5)); 35 | } 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(JUnit5_03_NewFeaturesAdvanced.class); 38 | 39 | 40 | //------------------------------------------------------------------------------------------------------------------------ 41 | // Test-Parameters 42 | //------------------------------------------------------------------------------------------------------------------------ 43 | 44 | /** 45 | * A simple example of a {@link ParameterResolver}-Implementation. @{@link ExtendWith} is used to mark {@link ClassName_ParameterResolver} 46 | * and {@link ParameterIndex_ParameterResolver} as used {@link ParameterResolver}. These could alternatively be placed at class level. 47 | * 48 | * @param className String-Parameter that will be injected by {@link ClassName_ParameterResolver} 49 | * @param parameterIndex Long-Parameter that will be injected by {@link ParameterIndex_ParameterResolver} 50 | */ 51 | @Test 52 | @ExtendWith({ClassName_ParameterResolver.class, ParameterIndex_ParameterResolver.class}) 53 | void customParameterTest(String className, Long parameterIndex) { 54 | LOG.info(className); // Surrounding class name injected by ClassName_ParameterResolver 55 | LOG.info(String.valueOf(parameterIndex)); // Parameter-Index injected by ParameterIndex_ParameterResolver 56 | } 57 | 58 | 59 | //------------------------------------------------------------------------------------------------------------------------ 60 | // Test-Factories 61 | //------------------------------------------------------------------------------------------------------------------------ 62 | 63 | /** 64 | * An example for a {@link TestFactory} with JUnit 5. 65 | * {@link DynamicTest#stream(Iterator, Function, ThrowingConsumer)} provides an easy way to factorize multiple tests, which will be executed automatically. 66 | * 67 | * It's basically similar to a for-loop that reads data and asserts, but these test will be grouped and displayed separately in the test results. 68 | * 69 | * @return A stream of dynamic tests 70 | */ 71 | @TestFactory 72 | Stream testStreamFactoryTest() { 73 | Iterator testData = Arrays.asList(new String[]{"1", "2", "3"}).iterator(); 74 | 75 | return DynamicTest.stream( 76 | testData, // Input-Data for the Factory 77 | s -> "Displayname: S" + s, // Creating DisplayNames for the test 78 | Assertions::assertNotNull); // Providing an Executable on which the test is based 79 | } 80 | 81 | /** 82 | * You can also pass table data 83 | * 84 | * @return A stream of dynamic tests with table data 85 | */ 86 | @TestFactory 87 | Stream testStreamFactoryTableDataTest() { 88 | List testData = Arrays.stream(new Object[][]{ 89 | {"Col 1, Row 1", "Col 2, Row 1", dummyFruits.get(1).getType().toString()}, 90 | {"Col 1, Row 2", "Col 2, Row 2", dummyFruits.get(2).getType().toString()} 91 | }) 92 | .map(objects -> Arrays.stream(objects).map(Object::toString).toArray(String[]::new)) 93 | .collect(Collectors.toList()); 94 | 95 | return testData.stream() 96 | .map(strings -> DynamicTest.dynamicTest(testData.toString(), () -> Assertions.assertNotNull(testData))); 97 | } 98 | 99 | 100 | //------------------------------------------------------------------------------------------------------------------------ 101 | // Test-Extensions 102 | //------------------------------------------------------------------------------------------------------------------------ 103 | 104 | /** 105 | * All possible extensions are implementations of {@link Extension} or its 106 | * extensions since those are required for {@link ExtendWith} or {@link Extensions}. 107 | * 108 | * The default implementations are currently inside {@link org.junit.jupiter.api.extension} and a list of them 109 | * inside the JavaDoc of {@link ExtendWith}. 110 | */ 111 | @Test 112 | void extensionInfoTest() {} 113 | 114 | /** 115 | * For this example I use my implementation of {@link ExecutionCondition} called {@link DisabledOnMonday} to 116 | * tell JUnit to disable this test on mondays, because who likes those, right? 117 | * 118 | * This annotation might just as well be placed on class level. To see how I implemented this look at 119 | * {@link DisabledOnMonday}. 120 | */ 121 | @Test 122 | @ExtendWith(DisabledOnMonday.class) 123 | void disabledOnMondayTest() {} 124 | } 125 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_04_AdvancedExamples.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5; 2 | 3 | import com.dmitrijdrandarov.junit5.utils.benchmarked.BenchmarkExtension; 4 | import com.dmitrijdrandarov.junit5.utils.benchmarked.Benchmarked; 5 | import com.dmitrijdrandarov.junit5.utils.simpleextension.DisabledOnWeekday; 6 | import com.dmitrijdrandarov.junit5.utils.simpleextension.DisabledWeekdays; 7 | import com.dmitrijdrandarov.junit5.utils.testannotationextension.UITest; 8 | import javafx.scene.layout.Pane; 9 | import org.junit.jupiter.api.Disabled; 10 | import org.junit.jupiter.api.Test; 11 | import org.junit.jupiter.api.extension.ExtendWith; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.util.Calendar; 16 | import java.util.stream.IntStream; 17 | import java.util.stream.LongStream; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | 21 | /** 22 | * A class containing advanced test samples and utils of features introduced and explained before. 23 | * 24 | * @author dmitrij-drandarov 25 | * @since 01 Aug 2016 26 | */ 27 | class JUnit5_04_AdvancedExamples { 28 | 29 | private static final Logger LOG = LoggerFactory.getLogger(JUnit5_04_AdvancedExamples.class); 30 | 31 | 32 | //------------------------------------------------------------------------------------------------------------------------ 33 | // Advanced Examples 34 | //------------------------------------------------------------------------------------------------------------------------ 35 | 36 | /** 37 | * Here I go a step further and annotate my days dynamically, by specifying the days I don't want the test to run 38 | * on with another custom annotation called @{@link DisabledWeekdays}. 39 | * 40 | * My extension {@link DisabledOnWeekday} later searches for @{@link DisabledWeekdays} and determines whether the 41 | * test should run or not. 42 | */ 43 | @Test 44 | @DisabledWeekdays({Calendar.THURSDAY, Calendar.SATURDAY}) 45 | @ExtendWith(DisabledOnWeekday.class) 46 | void disabledOnWeekdaysTest() {} 47 | 48 | /** 49 | * Here I use an annotation @{@link UITest} that is annotated by @{@link Test} itself, so it will be executed 50 | * properly. @{@link UITest} contains grouped information and annotations about this test like predefined 51 | * extensions. Further information in @{@link UITest}s JavaDoc. 52 | *# 53 | * This of course could be also possible for the examples above. 54 | */ 55 | @Disabled("Incompatible with JDK 12") 56 | @UITest("/fxml/sample.fxml") 57 | void userInterfaceTest(Pane root) { 58 | LOG.info(String.valueOf(root.getPrefWidth())); // 555.0 (defined in FXML-File) 59 | LOG.info(String.valueOf(root.getPrefHeight())); // 333.0 (defined in FXML-File) 60 | } 61 | 62 | /** 63 | * For this example I wrote an annotation @{@link Benchmarked} that doesn't include @{@link Test} - which it could - 64 | * but instead only contains an self-written extension called {@link BenchmarkExtension}. Annotating your class 65 | * with this will basically provide you with automatic benchmarking. 66 | * 67 | * This could of course be also placed on top of the class. 68 | */ 69 | @Test 70 | @Benchmarked 71 | void benchmarkedTest() { 72 | LOG.info("Calculating some primes..."); 73 | int primeCount = 200000; 74 | 75 | assertEquals(primeCount, IntStream.iterate(2, i -> i + 1) 76 | .filter(i -> LongStream.rangeClosed(2, (long) (Math.sqrt(i))).allMatch(n -> i % n != 0)) 77 | .limit(primeCount).toArray().length); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/benchmarked/BenchmarkExtension.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.benchmarked; 2 | 3 | import org.junit.jupiter.api.extension.*; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.sql.Date; 8 | import java.text.DateFormat; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | import static java.lang.System.currentTimeMillis; 13 | 14 | /** 15 | * Extension, that does the logging for the benchmarks. (Implementation is not accurate or performant!) 16 | * 17 | * @author dmitrij-drandarov 18 | * @since 29 Jul 2016 19 | */ 20 | public class BenchmarkExtension implements BeforeAllCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, AfterAllCallback { 21 | 22 | private static final Logger LOG = LoggerFactory.getLogger(BenchmarkExtension.class); 23 | 24 | private static final String APD = "\t-\t"; 25 | 26 | private static final Map startTime = new HashMap<>(); 27 | private static final DateFormat dtForm = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); 28 | 29 | @Override 30 | public void beforeAll(ExtensionContext context) throws Exception { 31 | String disp = context.getDisplayName(); 32 | long start = currentTimeMillis(); 33 | 34 | LOG.info("#### Summary \t" + APD + disp + " ####"); 35 | LOG.info("#### Start of Benchmark\t" + APD + disp + APD + dtForm.format(new Date(start)) + " ####"); 36 | startTime.put(disp, start); 37 | } 38 | 39 | @Override 40 | public void beforeTestExecution(ExtensionContext context) throws Exception { 41 | String disp = context.getDisplayName(); 42 | long start = currentTimeMillis(); 43 | 44 | LOG.info("#### Method-Benchm. ####" + APD + disp + APD + dtForm.format(new Date(start))); 45 | startTime.put(context.getDisplayName(), start); 46 | } 47 | 48 | @Override 49 | public void afterTestExecution(ExtensionContext context) throws Exception { 50 | String disp = context.getDisplayName(); 51 | long end = currentTimeMillis(); 52 | 53 | LOG.info("#### Summary ####" + APD + disp); 54 | LOG.info("#### Start ####" + APD + dtForm.format(new Date(startTime.get(disp)))); 55 | LOG.info("#### End ####" + APD + dtForm.format(new Date(end))); 56 | LOG.info("#### Duration ####" + APD + (end - startTime.get(disp)) + " ms\n"); 57 | } 58 | 59 | @Override 60 | public void afterAll(ExtensionContext context) throws Exception { 61 | String disp = context.getDisplayName(); 62 | long end = currentTimeMillis(); 63 | 64 | LOG.info("#### End of Benchmark \t" + APD + disp + APD + dtForm.format(new Date(end)) + " ####"); 65 | LOG.info("#### Duration for class\t" + APD + disp + APD + (end - startTime.get(disp)) + " ms ####"); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/benchmarked/Benchmarked.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.benchmarked; 2 | 3 | import org.junit.jupiter.api.extension.ExtendWith; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Annotation, that will mark the test method or class for benchmarking. 12 | * 13 | * @author dmitrij-drandarov 14 | * @since 29 Jul 2016 15 | */ 16 | @Target({ ElementType.TYPE, ElementType.METHOD }) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @ExtendWith(BenchmarkExtension.class) 19 | public @interface Benchmarked { } -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/parameterresolver/ClassName_ParameterResolver.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.parameterresolver; 2 | 3 | import org.junit.jupiter.api.extension.ExtensionContext; 4 | import org.junit.jupiter.api.extension.ParameterContext; 5 | import org.junit.jupiter.api.extension.ParameterResolutionException; 6 | import org.junit.jupiter.api.extension.ParameterResolver; 7 | 8 | /** 9 | * See {@link ParameterResolver}-JavaDoc. 10 | * 11 | * @author dmitrij-drandarov 12 | * @since 25 Jul 2016 13 | */ 14 | public class ClassName_ParameterResolver implements ParameterResolver { 15 | 16 | /** 17 | * Simple example that only checks if the Parameter-Type is a {@link String} based on the Parameter-Context to 18 | * determine whether the Parameter is supported by this {@link ParameterResolver}. 19 | */ 20 | @Override 21 | public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 22 | return parameterContext.getParameter().getType().equals(String.class); 23 | } 24 | 25 | /** 26 | * Simple example that simply resolves the Parameter by returning the Class-Name based on the Parameter-Context. 27 | */ 28 | @Override 29 | public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 30 | Class contextClass = extensionContext.getTestClass().orElse(null); 31 | 32 | return contextClass == null ? null : contextClass.getSimpleName(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/parameterresolver/ParameterIndex_ParameterResolver.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.parameterresolver; 2 | 3 | import org.junit.jupiter.api.extension.ExtensionContext; 4 | import org.junit.jupiter.api.extension.ParameterContext; 5 | import org.junit.jupiter.api.extension.ParameterResolutionException; 6 | import org.junit.jupiter.api.extension.ParameterResolver; 7 | 8 | /** 9 | * See {@link ParameterResolver}-JavaDoc. 10 | * 11 | * @author dmitrij-drandarov 12 | * @since 25 Jul 2016 13 | */ 14 | public class ParameterIndex_ParameterResolver implements ParameterResolver { 15 | 16 | /** 17 | * Simple example that only checks if the Parameter-Type is a {@link Long} based on the Parameter-Context to 18 | * determine whether the Parameter is supported by this {@link ParameterResolver}. 19 | */ 20 | @Override 21 | public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 22 | return parameterContext.getParameter().getType().equals(Long.class); 23 | } 24 | 25 | /** 26 | * Simple example that simply resolves the Parameter by returning the parameterIndex based on the Parameter-Context. 27 | */ 28 | @Override 29 | public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 30 | return (long) parameterContext.getIndex(); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/simpleextension/DisabledOnMonday.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.simpleextension; 2 | 3 | import org.junit.jupiter.api.extension.ConditionEvaluationResult; 4 | import org.junit.jupiter.api.extension.ExecutionCondition; 5 | import org.junit.jupiter.api.extension.ExtensionContext; 6 | 7 | import java.util.Calendar; 8 | 9 | /** 10 | * An extension that disables a test class on Mondays, because nobody likes those, right? 11 | * 12 | * @author dmitrij-drandarov 13 | * @since 28 Jul 2016 14 | */ 15 | public class DisabledOnMonday implements ExecutionCondition { 16 | 17 | @Override 18 | public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { 19 | boolean monday = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY; 20 | 21 | return monday ? 22 | ConditionEvaluationResult.disabled("I spare you on Mondays.") : 23 | ConditionEvaluationResult.enabled("Don't spare you on other days though >:("); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/simpleextension/DisabledOnWeekday.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.simpleextension; 2 | 3 | import org.junit.jupiter.api.extension.ConditionEvaluationResult; 4 | import org.junit.jupiter.api.extension.ExecutionCondition; 5 | import org.junit.jupiter.api.extension.ExtensionContext; 6 | 7 | import java.lang.reflect.AnnotatedElement; 8 | import java.util.Calendar; 9 | import java.util.Optional; 10 | import java.util.stream.IntStream; 11 | 12 | /** 13 | * An extension that disables this test class on the weekday specified by {@link DisabledWeekdays}. 14 | * 15 | * @author dmitrij-drandarov 16 | * @since 28 Jul 2016 17 | */ 18 | public class DisabledOnWeekday implements ExecutionCondition { 19 | 20 | @Override 21 | public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { 22 | 23 | // Search for the @DisabledWeekdays annotation from the TestExtensionContext 24 | Optional contextElement = context.getElement(); 25 | AnnotatedElement annotatedElement = contextElement.orElse(null); 26 | 27 | if (annotatedElement == null) return null; 28 | 29 | DisabledWeekdays weekdayAnnotation = annotatedElement.getAnnotation(DisabledWeekdays.class); 30 | 31 | // Determine whether the test should be disabled 32 | boolean weekdayToday = IntStream.of(weekdayAnnotation.value()) 33 | .anyMatch(day -> day == Calendar.getInstance().get(Calendar.DAY_OF_WEEK)); 34 | 35 | // Return a ConditionEvaluationResult based on the outcome of the boolean weekdayToday 36 | return weekdayToday ? 37 | ConditionEvaluationResult.disabled("I spare you today.") : 38 | ConditionEvaluationResult.enabled("Don't spare you on other days though >:("); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/simpleextension/DisabledWeekdays.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.simpleextension; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * A simple annotation to retain information about weekdays that the annotated tests are disabled on. 8 | * Used by {@link DisabledOnWeekday}-Extension. 9 | * 10 | * @author dmitrij-drandarov 11 | * @since 28 Jul 2016 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface DisabledWeekdays { 15 | int[] value(); 16 | } 17 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/testannotationextension/PrintUITestData.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.testannotationextension; 2 | 3 | import org.junit.jupiter.api.extension.BeforeEachCallback; 4 | import org.junit.jupiter.api.extension.ExtensionContext; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.lang.reflect.AnnotatedElement; 9 | import java.util.Optional; 10 | 11 | /** 12 | * Prints some information about the Test annotated by @{@link UITest}. 13 | * 14 | * @author dmitrij-drandarov 15 | * @since 29 Jul 2016 16 | */ 17 | class PrintUITestData implements BeforeEachCallback { 18 | 19 | private static final Logger LOG = LoggerFactory.getLogger(PrintUITestData.class); 20 | 21 | @Override 22 | public void beforeEach(ExtensionContext context) throws Exception { 23 | Optional contextElement = context.getElement(); 24 | AnnotatedElement annotatedElement = contextElement.orElse(null); 25 | 26 | if (annotatedElement != null) { 27 | UITest uiTest = annotatedElement.getAnnotation(UITest.class); 28 | LOG.info("Doing some setup for " + uiTest.value()); 29 | } 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/testannotationextension/RootElementResolver.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.testannotationextension; 2 | 3 | import javafx.fxml.FXMLLoader; 4 | import javafx.scene.layout.Pane; 5 | import org.junit.jupiter.api.extension.ExtensionContext; 6 | import org.junit.jupiter.api.extension.ParameterContext; 7 | import org.junit.jupiter.api.extension.ParameterResolutionException; 8 | import org.junit.jupiter.api.extension.ParameterResolver; 9 | 10 | import java.io.IOException; 11 | import java.lang.reflect.AnnotatedElement; 12 | 13 | /** 14 | * Resolves a {@link Pane} parameter by loading it from the fxml-path in {@link UITest#value()} 15 | * 16 | * @author dmitrij-drandarov 17 | * @since 29 Jul 2016 18 | */ 19 | class RootElementResolver implements ParameterResolver { 20 | 21 | @Override 22 | public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 23 | return extensionContext.getTags().contains("userInterface") 24 | & parameterContext.getParameter().getType().equals(Pane.class); 25 | } 26 | 27 | @Override 28 | public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 29 | AnnotatedElement annotatedElement = extensionContext.getElement().orElse(null); 30 | 31 | if (annotatedElement == null) return null; 32 | 33 | UITest annotation = annotatedElement.getAnnotation(UITest.class); 34 | FXMLLoader loader = new FXMLLoader(getClass().getResource(annotation.value())); 35 | 36 | try { 37 | return loader.load(); 38 | } catch (IOException e) { 39 | return null; 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/utils/testannotationextension/UITest.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5.utils.testannotationextension; 2 | 3 | import org.junit.jupiter.api.Tag; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | /** 13 | * Test annotated by this will be executed by the test runner without problems due to @{@link Test} being included. 14 | * You can basically group annotations by doing this and save some space, by not having to add all those 15 | * {@link ExtendWith}s etc. to each method. 16 | * Readability inside the test classes is the key here. And it looks cooler ;) 17 | * 18 | * @author dmitrij-drandarov 19 | * @since 29 Jul 2016 20 | */ 21 | @Test 22 | @Tag("userInterface") // For simple identification by ParameterResolvers 23 | @ExtendWith(PrintUITestData.class) // Prints UI Test Data before each test 24 | @ExtendWith(RootElementResolver.class) // Resolves the root pane 25 | @Target(ElementType.METHOD) 26 | @Retention(RetentionPolicy.RUNTIME) // Required for the test to be automatically executed 27 | public @interface UITest { 28 | 29 | /** 30 | * FXML-Path. 31 | * 32 | * @return FXML-Path used for the UI-Test. 33 | */ 34 | String value(); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /01-junit-5/src/test/resources/fxml/sample.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /02-assertj/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | 5 | 6 | // AssertJ 7 | testCompile "org.assertj:assertj-core:$assertj_version" 8 | 9 | 10 | // Hamcrest for comparison 11 | testCompile "org.hamcrest:hamcrest-all:$hamcrest_version" 12 | 13 | 14 | // Commons 15 | testCompile project(":common") 16 | 17 | 18 | // Logging 19 | testCompile "org.slf4j:slf4j-simple:$slf4j_version" 20 | } 21 | -------------------------------------------------------------------------------- /02-assertj/src/test/java/com.dmitrijdrandarov/assertj/AssertJ_01_Comparison.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.assertj; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import com.dmitrijdrandarov.entities.DummyObject; 5 | import com.dmitrijdrandarov.utils.DummyFruitFactory; 6 | import org.assertj.core.api.Assertions; 7 | import org.hamcrest.MatcherAssert; 8 | import org.hamcrest.Matchers; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.Arrays; 15 | import java.util.Collections; 16 | import java.util.List; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertTrue; 19 | 20 | class AssertJ_01_Comparison { 21 | 22 | private static final Logger LOG = LoggerFactory.getLogger(AssertJ_01_Comparison.class); 23 | 24 | private List dummyFruits; 25 | 26 | @BeforeEach 27 | void setUp() { 28 | DummyFruitFactory dummyFruitFactory = new DummyFruitFactory(); 29 | dummyFruits = dummyFruitFactory.createDummyFruits(); 30 | } 31 | 32 | /** 33 | * Hard to read and failed assertion messages are meaningless. 34 | * 35 | * Let's not even talk about Java 7 and downwards. 36 | */ 37 | @Test 38 | void objectHasPropertyJava8() { 39 | // Java 8 40 | assertTrue(dummyFruits.stream().map(DummyObject::getName).anyMatch("Baby Banana"::equals) 41 | && dummyFruits.stream().map(DummyObject::getName).anyMatch("Granny Smith Apple"::equals) 42 | && dummyFruits.stream().map(DummyObject::getName).anyMatch("Grapefruit"::equals)); 43 | 44 | 45 | // Failed assertion message meaningless 46 | 47 | // org.opentest4j.AssertionFailedError: 48 | 49 | } 50 | 51 | /** 52 | * Better, but still hard to read. Failed assertion messages are ok. 53 | * 54 | * Framework hasn't been updated in a while. No longer bundled in JUnit 5 like it was in JUnit 4. 55 | */ 56 | @Test 57 | void objectHasPropertyHamcrest() { 58 | // Hamcrest not type-safe. Hard to infer because of reflections (somewhat adressed in JDK 9) 59 | MatcherAssert.assertThat(dummyFruits, 60 | Matchers.anyOf(Matchers.contains( // No type-safety here 61 | Matchers.hasProperty("name", Matchers.is("Baby Banana")), 62 | Matchers.hasProperty("name", Matchers.is("Granny Smith Apple")), 63 | Matchers.hasProperty("name", Matchers.is("Grapefruit"))) 64 | )); 65 | 66 | // Hamcrest, type-safe 67 | MatcherAssert.assertThat(dummyFruits, 68 | Matchers.anyOf(Collections.singletonList(Matchers.contains(Arrays.asList( // Safe, but very ugly... 69 | Matchers.hasProperty("name", Matchers.is("Baby Banana")), 70 | Matchers.hasProperty("name", Matchers.is("Granny Smith Apple")), 71 | Matchers.hasProperty("name", Matchers.is("Grapefruit"))) 72 | )))); 73 | 74 | 75 | // Hard to read, but at least it states the problem in case assertion fails in a somewhat readable form. 76 | 77 | /* 78 | java.lang.AssertionError: 79 | Expected: (iterable containing [hasProperty("name", is "Baby Banana"), hasProperty("name", is "Granny Smith Apple"), hasProperty("name", is "FailedFruit")]) 80 | but: was <[1, Baby Banana, It's yellow!, 20.0, BANANA, 2, Granny Smith Apple, Delicious!, 10.5, APPLE, 3, Grapefruit, It's totally an orange, baka!, 8.5, ORANGE]> 81 | */ 82 | } 83 | 84 | /** 85 | * Short, readable, meaningful failed assertion message. 86 | * 87 | * Framework is frequently updated. 88 | */ 89 | @Test 90 | void objectHasPropertyAssertJ() { 91 | // AssertJ 92 | Assertions.assertThat(dummyFruits) 93 | .extracting(DummyObject::getName) 94 | .contains("Baby Banana", "Granny Smith Apple", "Grapefruit"); 95 | 96 | 97 | // Great message in case assertion fails 98 | 99 | /* 100 | java.lang.AssertionError: 101 | Expecting: 102 | <["Baby Banana", "Granny Smith Apple", "Grapefruit"]> 103 | to contain: 104 | <["Baby Banana", "Granny Smith Apple", "FailedFruit"]> 105 | but could not find: 106 | <["FailedFruit"]> 107 | */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /02-assertj/src/test/java/com.dmitrijdrandarov/assertj/AssertJ_02_FluentAssertionsAndMoreFeatures.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.assertj; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import com.dmitrijdrandarov.entities.DummyObject; 5 | import com.dmitrijdrandarov.utils.DummyFruitFactory; 6 | import org.assertj.core.api.Condition; 7 | import org.assertj.core.api.SoftAssertions; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | import java.time.Instant; 14 | import java.util.List; 15 | 16 | import static org.assertj.core.api.Assertions.*; 17 | import static org.assertj.core.api.AssertionsForClassTypes.tuple; 18 | 19 | class AssertJ_02_FluentAssertionsAndMoreFeatures { 20 | 21 | private static final Logger LOG = LoggerFactory.getLogger(AssertJ_01_Comparison.class); 22 | 23 | private List dummyFruits; 24 | private DummyFruit babyBanana; 25 | private DummyFruit grannySmithApple; 26 | private DummyFruit grapefruit; 27 | 28 | private List otherDummyFruitList; 29 | private DummyFruit redBanana; 30 | 31 | @BeforeEach 32 | void setUp() { 33 | DummyFruitFactory dummyFruitFactory = new DummyFruitFactory(); 34 | dummyFruits = dummyFruitFactory.createDummyFruits(); 35 | otherDummyFruitList = dummyFruitFactory.createDummyFruits2(); 36 | 37 | babyBanana = dummyFruitFactory.createBabyBanana(); 38 | grannySmithApple = dummyFruitFactory.createGrannySmithApple(); 39 | grapefruit = dummyFruitFactory.createGrapefruit(); 40 | redBanana = dummyFruitFactory.createRedBanana(); 41 | } 42 | 43 | 44 | //------------------------------------------------------------------------------------------------------------------------ 45 | // Fluent assertions 46 | //------------------------------------------------------------------------------------------------------------------------ 47 | 48 | @Test 49 | void fluentAssertionsWithDifferentTypes() { 50 | 51 | // Very readable, but a bit too much overhead for this use case 52 | assertThat(babyBanana.getName()).isEqualTo("Baby Banana"); // StringAssert 53 | assertThat(babyBanana).isNotEqualTo(grannySmithApple); // ObjectAssert 54 | 55 | 56 | // Different AssertTypes 57 | 58 | // Multiple string-specific assertions chained together 59 | assertThat(babyBanana.getName()) 60 | .startsWith("Bab") 61 | .endsWith("nana") 62 | .isEqualToIgnoringCase("baby banana"); 63 | 64 | // Multiple collection-specific assertions chained together 65 | assertThat(dummyFruits) 66 | .hasSize(3) 67 | .contains(babyBanana, grapefruit) 68 | .doesNotContain(redBanana) 69 | .hasOnlyElementsOfType(DummyFruit.class); 70 | 71 | assertThat(otherDummyFruitList) 72 | .containsAll(dummyFruits); 73 | 74 | // Collection-specific assertions with custom conditions 75 | assertThat(dummyFruits) 76 | .allMatch(dummyFruit -> dummyFruit.getName().contains("a")); 77 | 78 | assertThat(dummyFruits) 79 | .areExactly(2, new Condition<>(dummyFruit -> dummyFruit.getName().contains("t"), "contains t")); 80 | 81 | assertThat(dummyFruits) 82 | .extracting(DummyObject::getName) 83 | .areExactly(2, new Condition<>(dummyName -> dummyName.contains("t"), "contains t")); 84 | 85 | // Temporal-specific assertions chained together 86 | Instant instant = Instant.now(); 87 | Instant inOneMinute = instant.plusSeconds(60); 88 | Instant beforeOneMinute = instant.minusSeconds(60); 89 | assertThat(instant) 90 | .isBetween(instant.minusSeconds(1), instant.plusSeconds(1)) 91 | .isBetween(instant.minusSeconds(1), inOneMinute) 92 | .isBetween(beforeOneMinute, instant.plusSeconds(1)) 93 | .isBetween(beforeOneMinute, inOneMinute); 94 | 95 | } 96 | 97 | 98 | //------------------------------------------------------------------------------------------------------------------------ 99 | // Working with properties/fields 100 | //------------------------------------------------------------------------------------------------------------------------ 101 | 102 | @Test 103 | void workingWithProperties() { 104 | // Check if list contains elements with specific properties (Java 7 reflection) 105 | assertThat(dummyFruits) 106 | .extracting("name") 107 | .containsExactly("Baby Banana", "Granny Smith Apple", "Grapefruit"); 108 | 109 | // Check if list contains elements with specific properties (Java 8 method reference) 110 | assertThat(dummyFruits) 111 | .extracting(DummyFruit::getName) 112 | .doesNotContain("Red Banana"); 113 | 114 | // Extract multiple values 115 | assertThat(dummyFruits).extracting(DummyFruit::getName, DummyFruit::getType) 116 | .containsExactly( 117 | tuple("Baby Banana", DummyFruit.TYPE.BANANA), 118 | tuple("Granny Smith Apple", DummyFruit.TYPE.APPLE), 119 | tuple("Grapefruit", DummyFruit.TYPE.ORANGE)); 120 | } 121 | 122 | 123 | //------------------------------------------------------------------------------------------------------------------------ 124 | // Working with filtering 125 | //------------------------------------------------------------------------------------------------------------------------ 126 | 127 | @Test 128 | void workingWithFiltering() { 129 | // Filtering Examples 130 | assertThat(dummyFruits) 131 | .filteredOn(fruit -> fruit.getName().contains("t")) 132 | .containsOnly(grapefruit, grannySmithApple); 133 | 134 | assertThat(otherDummyFruitList) 135 | .filteredOn(fruit -> fruit.getName().contains("Bana")) 136 | .containsExactly(babyBanana, redBanana) 137 | .extracting(DummyFruit::getType) 138 | .containsOnly(DummyFruit.TYPE.BANANA); 139 | } 140 | 141 | 142 | //------------------------------------------------------------------------------------------------------------------------ 143 | // Working with exceptions 144 | //------------------------------------------------------------------------------------------------------------------------ 145 | 146 | @Test 147 | void workingWithExceptions() { 148 | // Alternative to assertThrows of JUnit 5 with fluent api 149 | assertThatThrownBy(() -> { throw new Exception("I'm an exception!"); }).hasMessage("I'm an exception!"); 150 | 151 | // Or get the Throwable as an instance 152 | Throwable thrown = catchThrowable(() -> { throw new Exception("I'm an exception!"); }); 153 | assertThat(thrown).hasMessageContaining("I'm an exception!"); // ThrowableAssert 154 | } 155 | 156 | 157 | //------------------------------------------------------------------------------------------------------------------------ 158 | // Soft Assertions 159 | //------------------------------------------------------------------------------------------------------------------------ 160 | 161 | /** 162 | * SoftAssertions are used to group assertions with different assertion bases and force all of them to run even if a previous assertion failed. 163 | */ 164 | @Test 165 | void softAssertion() { 166 | SoftAssertions.assertSoftly(soft -> { 167 | // Base dummyFruits 168 | soft.assertThat(dummyFruits) 169 | .hasSize(3) 170 | .containsExactly(babyBanana, grannySmithApple, grapefruit); 171 | // Base otherDummyFruitList 172 | soft.assertThat(otherDummyFruitList) 173 | .hasSize(4) 174 | .containsAll(dummyFruits) 175 | .containsExactly(babyBanana, grannySmithApple, grapefruit, redBanana); 176 | // Base 177 | soft.assertThat(grapefruit).extracting(DummyFruit::getType).allMatch(DummyFruit.TYPE.ORANGE::equals); 178 | }); 179 | } 180 | 181 | 182 | //------------------------------------------------------------------------------------------------------------------------ 183 | // Descriptions 184 | //------------------------------------------------------------------------------------------------------------------------ 185 | 186 | @Test 187 | void descriptionsWithAs() { 188 | // Description with .as() will be shown before the error message 189 | assertThat(babyBanana.getValue()).as("check %s's value", babyBanana.getName()).isEqualTo(20.0); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /03-spring/03-01-spring-boot-4/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | 5 | // Lagacy support for JUnit 4 6 | testCompile "org.junit.vintage:junit-vintage-engine:$junit5_vintage4_version" 7 | testCompile "junit:junit:$junit4_version" 8 | 9 | // Spring Boot 4 10 | compile "org.springframework.boot:spring-boot-starter-data-jpa:$spring4_boot_version" 11 | testCompile "org.springframework.boot:spring-boot-starter-test:$spring4_boot_version" 12 | // JUnit 5 Extension for Spring 4 Tests 13 | testCompile "com.github.sbrannen:spring-test-junit5:$junit5_spring4_test_version" 14 | 15 | 16 | // Commons 17 | testCompile project(":common") 18 | 19 | // Database Drivers 20 | runtime "org.hsqldb:hsqldb:$hsqldb_driver_version" 21 | runtime "org.postgresql:postgresql:$postgresql_driver_version" 22 | } -------------------------------------------------------------------------------- /03-spring/03-01-spring-boot-4/src/main/java/com/dmitrijdrandarov/SpringApplication.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | /** 6 | * Needed for @SpringBootTest 7 | */ 8 | @SpringBootApplication 9 | public class SpringApplication { 10 | } 11 | -------------------------------------------------------------------------------- /03-spring/03-01-spring-boot-4/src/test/java/com/dmitrijdrandarov/junit4/SpringBoot4_01_JUnit4Test.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit4; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import com.dmitrijdrandarov.repositories.DummyFruitRepository; 5 | import org.junit.Assert; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; 13 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import java.util.List; 19 | 20 | @SpringBootTest 21 | @RunWith(SpringRunner.class) 22 | @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.HSQL) 23 | @Transactional 24 | @Ignore("Incompatible with JDK 12") 25 | public class SpringBoot4_01_JUnit4Test { 26 | 27 | private static final Logger LOG = LoggerFactory.getLogger(SpringBoot4_01_JUnit4Test.class); 28 | 29 | @Autowired 30 | private DummyFruitRepository fruitRepository; 31 | 32 | @Test 33 | public void saveTest() { 34 | DummyFruit fruity = fruitRepository.save(new DummyFruit(null, DummyFruit.TYPE.APPLE, "Fruity", "Description", 10D)); 35 | fruity = fruitRepository.findOneById(fruity.getId()); 36 | Assert.assertNotNull(fruity); 37 | 38 | LOG.info(fruity.toString()); 39 | } 40 | 41 | @Test 42 | public void dataSQLTest() { 43 | List dummyFruits = fruitRepository.findAllBy(); 44 | Assert.assertEquals(3, dummyFruits.size()); 45 | 46 | dummyFruits.stream().map(DummyFruit::toString).forEach(LOG::info); 47 | } 48 | } -------------------------------------------------------------------------------- /03-spring/03-01-spring-boot-4/src/test/java/com/dmitrijdrandarov/junit5/SpringBoot4_02_JUnit5Test.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import com.dmitrijdrandarov.repositories.DummyFruitRepository; 5 | import org.junit.Assert; 6 | import org.junit.jupiter.api.Disabled; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; 13 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.test.context.junit.jupiter.SpringExtension; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import java.util.List; 19 | 20 | @SpringBootTest 21 | @ExtendWith(SpringExtension.class) 22 | @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.HSQL) 23 | @Transactional 24 | @Disabled("Incompatible with JDK 12") 25 | class SpringBoot4_02_JUnit5Test { 26 | 27 | private static final Logger LOG = LoggerFactory.getLogger(SpringBoot4_02_JUnit5Test.class); 28 | 29 | @Autowired 30 | private DummyFruitRepository fruitRepository; 31 | 32 | @Test 33 | void saveTest() { 34 | DummyFruit fruity = fruitRepository.save(new DummyFruit(null, DummyFruit.TYPE.APPLE, "Fruity", "Description", 10D)); 35 | fruity = fruitRepository.findOneById(fruity.getId()); 36 | Assert.assertNotNull(fruity); 37 | 38 | LOG.info(fruity.toString()); 39 | } 40 | 41 | @Test 42 | void dataSQLTest() { 43 | List dummyFruits = fruitRepository.findAllBy(); 44 | Assert.assertEquals(3, dummyFruits.size()); 45 | 46 | dummyFruits.stream().map(DummyFruit::toString).forEach(LOG::info); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /03-spring/03-01-spring-boot-4/src/test/java/com/dmitrijdrandarov/repositories/DummyFruitRepository.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.repositories; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface DummyFruitRepository extends CrudRepository { 9 | 10 | List findAllBy(); 11 | DummyFruit findOneById(Long id); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /03-spring/03-01-spring-boot-4/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | # If schema.sql is supposed to be used instead of hibernate dll generation 2 | spring.jpa.hibernate.ddl-auto=none 3 | 4 | # Or remove spring.datasource.schema below and create 5 | # spring.jpa.hibernate.ddl-auto=create-drop 6 | 7 | # Your database tables 8 | spring.datasource.schema=schema.sql 9 | # Test data if necessary 10 | spring.datasource.data=data.sql 11 | 12 | # Unimportant 13 | logging.pattern.console=%d{HH:mm:ss.SSS} %-5level %-6t %-40logger{40}- %msg%n -------------------------------------------------------------------------------- /03-spring/03-01-spring-boot-4/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO DummyFruit (name, description, value, type) VALUES 2 | ('Baby Banana', 'It is yellow!', 20.0, 'BANANA'), 3 | ('Granny Smith Apple', 'Delicious!', 10.5, 'APPLE'), 4 | ('Grapefruit', 'It is totally an orange, baka!', 8.5, 'ORANGE'); -------------------------------------------------------------------------------- /03-spring/03-01-spring-boot-4/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE DummyFruit ( 2 | id INTEGER IDENTITY PRIMARY KEY, 3 | name VARCHAR(256), 4 | description VARCHAR(256), 5 | value DOUBLE, 6 | type VARCHAR(256) 7 | ); -------------------------------------------------------------------------------- /03-spring/03-02-spring-boot-5/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | // Lagacy support for JUnit 4 5 | testCompile "org.junit.vintage:junit-vintage-engine:$junit5_vintage4_version" 6 | 7 | 8 | // Spring Boot 5 9 | compile "org.springframework.boot:spring-boot-starter-data-jpa:$spring5_boot_version" 10 | testCompile "org.springframework.boot:spring-boot-starter-test:$spring5_boot_version" 11 | 12 | 13 | // Commons 14 | testCompile project(":common") 15 | 16 | 17 | // Database Drivers 18 | runtime "org.hsqldb:hsqldb:$hsqldb_driver_version" 19 | runtime "org.postgresql:postgresql:$postgresql_driver_version" 20 | } -------------------------------------------------------------------------------- /03-spring/03-02-spring-boot-5/src/main/java/com/dmitrijdrandarov/SpringApplication.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | /** 6 | * Needed for @SpringBootTest 7 | */ 8 | @SpringBootApplication 9 | public class SpringApplication { 10 | } 11 | -------------------------------------------------------------------------------- /03-spring/03-02-spring-boot-5/src/test/java/com/dmitrijdrandarov/junit4/SpringBoot5_01_JUnit4Test.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit4; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import com.dmitrijdrandarov.repositories.DummyFruitRepository; 5 | import org.junit.Assert; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; 13 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import java.util.List; 19 | 20 | @SpringBootTest 21 | @RunWith(SpringRunner.class) 22 | @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.HSQL) 23 | @Transactional 24 | @Ignore 25 | public class SpringBoot5_01_JUnit4Test { 26 | 27 | private static final Logger LOG = LoggerFactory.getLogger(SpringBoot5_01_JUnit4Test.class); 28 | 29 | @Autowired 30 | private DummyFruitRepository fruitRepository; 31 | 32 | @Test 33 | public void saveTest() { 34 | DummyFruit fruity = fruitRepository.save(new DummyFruit(null, DummyFruit.TYPE.APPLE, "Fruity", "Description", 10D)); 35 | fruity = fruitRepository.findOneById(fruity.getId()); 36 | Assert.assertNotNull(fruity); 37 | 38 | LOG.info(fruity.toString()); 39 | } 40 | 41 | @Test 42 | public void dataSQLTest() { 43 | List dummyFruits = fruitRepository.findAllBy(); 44 | Assert.assertEquals(3, dummyFruits.size()); 45 | 46 | dummyFruits.stream().map(DummyFruit::toString).forEach(LOG::info); 47 | } 48 | } -------------------------------------------------------------------------------- /03-spring/03-02-spring-boot-5/src/test/java/com/dmitrijdrandarov/junit5/SpringBoot5_02_JUnit5Test.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.junit5; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import com.dmitrijdrandarov.repositories.DummyFruitRepository; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Disabled; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; 13 | import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.test.context.junit.jupiter.SpringExtension; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import java.util.List; 19 | 20 | @SpringBootTest 21 | @ExtendWith(SpringExtension.class) 22 | @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.HSQL) 23 | @Transactional 24 | @Disabled("Right now incompatible with JDK 12") 25 | class SpringBoot5_02_JUnit5Test { 26 | 27 | private static final Logger LOG = LoggerFactory.getLogger(SpringBoot5_02_JUnit5Test.class); 28 | 29 | @Autowired 30 | private DummyFruitRepository fruitRepository; 31 | 32 | @Test 33 | void saveTest() { 34 | DummyFruit fruity = fruitRepository.save(new DummyFruit(null, DummyFruit.TYPE.APPLE, "Fruity", "Description", 10D)); 35 | fruity = fruitRepository.findOneById(fruity.getId()); 36 | Assertions.assertNotNull(fruity); 37 | 38 | LOG.info(fruity.toString()); 39 | } 40 | 41 | @Test 42 | void dataSQLTest() { 43 | List dummyFruits = fruitRepository.findAllBy(); 44 | Assertions.assertEquals(3, dummyFruits.size()); 45 | 46 | dummyFruits.stream().map(DummyFruit::toString).forEach(LOG::info); 47 | } 48 | } -------------------------------------------------------------------------------- /03-spring/03-02-spring-boot-5/src/test/java/com/dmitrijdrandarov/repositories/DummyFruitRepository.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.repositories; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface DummyFruitRepository extends CrudRepository { 9 | 10 | List findAllBy(); 11 | DummyFruit findOneById(Long id); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /03-spring/03-02-spring-boot-5/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | # If schema.sql is supposed to be used instead of hibernate dll generation 2 | spring.jpa.hibernate.ddl-auto=none 3 | 4 | # Or remove spring.datasource.schema below and create 5 | # spring.jpa.hibernate.ddl-auto=create-drop 6 | 7 | # Your database tables 8 | spring.datasource.schema=schema.sql 9 | # Test data if necessary 10 | spring.datasource.data=data.sql 11 | 12 | # Unimportant 13 | logging.pattern.console=%d{HH:mm:ss.SSS} %-5level %-6t %-40logger{40}- %msg%n -------------------------------------------------------------------------------- /03-spring/03-02-spring-boot-5/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO DummyFruit (name, description, value, type) VALUES 2 | ('Baby Banana', 'It is yellow!', 20.0, 'BANANA'), 3 | ('Granny Smith Apple', 'Delicious!', 10.5, 'APPLE'), 4 | ('Grapefruit', 'It is totally an orange, baka!', 8.5, 'ORANGE'); -------------------------------------------------------------------------------- /03-spring/03-02-spring-boot-5/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE DummyFruit ( 2 | id INTEGER IDENTITY PRIMARY KEY, 3 | name VARCHAR(256), 4 | description VARCHAR(256), 5 | value DOUBLE, 6 | type VARCHAR(256) 7 | ); -------------------------------------------------------------------------------- /03-spring/03-03-spring-4/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | // Lagacy support for JUnit 4 5 | testCompile "org.junit.vintage:junit-vintage-engine:$junit5_vintage4_version" 6 | 7 | 8 | // Spring 4 9 | compile "org.springframework.data:spring-data-jpa:$spring4_version" 10 | // JUnit 5 Extension for Spring 4 Tests 11 | testCompile "com.github.sbrannen:spring-test-junit5:$junit5_spring4_test_version" 12 | 13 | 14 | // Hibernate JPA 15 | compile "org.hibernate:hibernate-core:$hibernate_core_version" 16 | 17 | 18 | // Commons 19 | compile project(":common") 20 | 21 | 22 | // Database Drivers 23 | runtime "org.hsqldb:hsqldb:$hsqldb_driver_version" 24 | runtime "org.postgresql:postgresql:$postgresql_driver_version" 25 | } -------------------------------------------------------------------------------- /03-spring/03-03-spring-4/src/test/java/com/dmitrijdrandarov/spring4/Spring4Config.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.spring4; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 6 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; 7 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 8 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 9 | import org.springframework.orm.jpa.JpaTransactionManager; 10 | import org.springframework.orm.jpa.JpaVendorAdapter; 11 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 12 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; 13 | import org.springframework.transaction.PlatformTransactionManager; 14 | 15 | import javax.persistence.EntityManagerFactory; 16 | import javax.sql.DataSource; 17 | 18 | 19 | @Configuration 20 | @EnableJpaRepositories(basePackages={"com.dmitrijdrandarov.spring4.repositories"}) 21 | public class Spring4Config { 22 | 23 | @Bean 24 | public DataSource dataSource() { 25 | EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); 26 | EmbeddedDatabase db = builder 27 | .setType(EmbeddedDatabaseType.HSQL) 28 | .addScript("schema.sql") 29 | .addScript("data.sql") 30 | .build(); 31 | return db; 32 | } 33 | 34 | @Bean 35 | public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 36 | LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); 37 | em.setDataSource(dataSource()); 38 | em.setPackagesToScan("com.dmitrijdrandarov"); 39 | 40 | JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 41 | em.setJpaVendorAdapter(vendorAdapter); 42 | 43 | return em; 44 | } 45 | 46 | @Bean 47 | public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ 48 | JpaTransactionManager transactionManager = new JpaTransactionManager(); 49 | transactionManager.setEntityManagerFactory(emf); 50 | 51 | return transactionManager; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /03-spring/03-03-spring-4/src/test/java/com/dmitrijdrandarov/spring4/junit4/Spring4_01_JUnit4Test.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.spring4.junit4; 2 | 3 | import com.dmitrijdrandarov.spring4.Spring4Config; 4 | import com.dmitrijdrandarov.entities.DummyFruit; 5 | import com.dmitrijdrandarov.spring4.repositories.DummyFruitRepository; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.test.context.ContextConfiguration; 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import java.util.List; 17 | 18 | @ContextConfiguration(classes = {Spring4Config.class}) 19 | @RunWith(SpringRunner.class) 20 | @Transactional 21 | public class Spring4_01_JUnit4Test { 22 | 23 | private static final Logger LOG = LoggerFactory.getLogger(Spring4_01_JUnit4Test.class); 24 | 25 | @Autowired 26 | private DummyFruitRepository fruitRepository; 27 | 28 | @Test 29 | public void saveTest() { 30 | DummyFruit fruity = fruitRepository.save(new DummyFruit(null, DummyFruit.TYPE.APPLE, "Fruity", "Description", 10D)); 31 | fruity = fruitRepository.findOneById(fruity.getId()); 32 | Assert.assertNotNull(fruity); 33 | 34 | // LOG.info(fruity.toString()); 35 | System.out.println(fruity.toString()); 36 | } 37 | 38 | @Test 39 | public void dataSQLTest() { 40 | List dummyFruits = fruitRepository.findAllBy(); 41 | Assert.assertEquals(3, dummyFruits.size()); 42 | 43 | // dummyFruits.stream().map(DummyFruit::toString).forEach(LOG::info); 44 | dummyFruits.stream().map(DummyFruit::toString).forEach(System.out::println); 45 | } 46 | } -------------------------------------------------------------------------------- /03-spring/03-03-spring-4/src/test/java/com/dmitrijdrandarov/spring4/junit5/Spring4_02_JUnit5Test.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.spring4.junit5; 2 | 3 | import com.dmitrijdrandarov.spring4.Spring4Config; 4 | import com.dmitrijdrandarov.entities.DummyFruit; 5 | import com.dmitrijdrandarov.spring4.repositories.DummyFruitRepository; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.test.context.ContextConfiguration; 13 | import org.springframework.test.context.junit.jupiter.SpringExtension; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import java.util.List; 17 | 18 | @ContextConfiguration(classes = {Spring4Config.class}) 19 | @ExtendWith(SpringExtension.class) 20 | @Transactional 21 | class Spring4_02_JUnit5Test { 22 | 23 | private static final Logger LOG = LoggerFactory.getLogger(Spring4_02_JUnit5Test.class); 24 | 25 | @Autowired 26 | private DummyFruitRepository fruitRepository; 27 | 28 | @Test 29 | void saveTest() { 30 | DummyFruit fruity = fruitRepository.save(new DummyFruit(null, DummyFruit.TYPE.APPLE, "Fruity", "Description", 10D)); 31 | fruity = fruitRepository.findOneById(fruity.getId()); 32 | Assertions.assertNotNull(fruity); 33 | 34 | // LOG.info(fruity.toString()); 35 | System.out.println(fruity.toString()); 36 | } 37 | 38 | @Test 39 | void dataSQLTest() { 40 | List dummyFruits = fruitRepository.findAllBy(); 41 | Assertions.assertEquals(3, dummyFruits.size()); 42 | 43 | // dummyFruits.stream().map(DummyFruit::toString).forEach(LOG::info); 44 | dummyFruits.stream().map(DummyFruit::toString).forEach(System.out::println); 45 | } 46 | } -------------------------------------------------------------------------------- /03-spring/03-03-spring-4/src/test/java/com/dmitrijdrandarov/spring4/repositories/DummyFruitRepository.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.spring4.repositories; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface DummyFruitRepository extends CrudRepository { 9 | 10 | List findAllBy(); 11 | DummyFruit findOneById(Long id); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /03-spring/03-03-spring-4/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO DummyFruit (name, description, value, type) VALUES 2 | ('Baby Banana', 'It is yellow!', 20.0, 'BANANA'), 3 | ('Granny Smith Apple', 'Delicious!', 10.5, 'APPLE'), 4 | ('Grapefruit', 'It is totally an orange, baka!', 8.5, 'ORANGE'); -------------------------------------------------------------------------------- /03-spring/03-03-spring-4/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE DummyFruit ( 2 | id INTEGER IDENTITY PRIMARY KEY, 3 | name VARCHAR(256), 4 | description VARCHAR(256), 5 | value DOUBLE, 6 | type VARCHAR(256) 7 | ); -------------------------------------------------------------------------------- /03-spring/03-04-spring-5/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | // Lagacy support for JUnit 4 5 | testCompile "org.junit.vintage:junit-vintage-engine:$junit5_vintage4_version" 6 | 7 | 8 | // Spring 5 9 | compile "org.springframework.data:spring-data-jpa:$spring5_data_jpa_version" 10 | testCompile "org.springframework:spring-test:$spring5_version" 11 | 12 | 13 | // Hibernate JPA 14 | compile "org.hibernate:hibernate-core:$hibernate_core_version" 15 | 16 | 17 | // Commons 18 | compile project(":common") 19 | 20 | 21 | // Database Drivers 22 | runtime "org.hsqldb:hsqldb:$hsqldb_driver_version" 23 | runtime "org.postgresql:postgresql:$postgresql_driver_version" 24 | } -------------------------------------------------------------------------------- /03-spring/03-04-spring-5/src/test/java/com/dmitrijdrandarov/spring5/Spring5Config.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.spring5; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 6 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; 7 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 8 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 9 | import org.springframework.orm.jpa.JpaTransactionManager; 10 | import org.springframework.orm.jpa.JpaVendorAdapter; 11 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 12 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; 13 | import org.springframework.transaction.PlatformTransactionManager; 14 | 15 | import javax.persistence.EntityManagerFactory; 16 | import javax.sql.DataSource; 17 | 18 | 19 | @Configuration 20 | @EnableJpaRepositories(basePackages={"com.dmitrijdrandarov.spring5.repositories"}) 21 | public class Spring5Config { 22 | 23 | @Bean 24 | public DataSource dataSource() { 25 | 26 | // no need shutdown, EmbeddedDatabaseFactoryBean will take care of this 27 | EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); 28 | EmbeddedDatabase db = builder 29 | .setType(EmbeddedDatabaseType.HSQL) //.H2 or .DERBY 30 | .addScript("schema.sql") 31 | .addScript("data.sql") 32 | .build(); 33 | return db; 34 | } 35 | 36 | @Bean 37 | public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 38 | LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); 39 | em.setDataSource(dataSource()); 40 | em.setPackagesToScan("com.dmitrijdrandarov"); 41 | 42 | JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 43 | em.setJpaVendorAdapter(vendorAdapter); 44 | 45 | return em; 46 | } 47 | 48 | @Bean 49 | public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ 50 | JpaTransactionManager transactionManager = new JpaTransactionManager(); 51 | transactionManager.setEntityManagerFactory(emf); 52 | 53 | return transactionManager; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /03-spring/03-04-spring-5/src/test/java/com/dmitrijdrandarov/spring5/junit4/Spring5_01_JUnit4Test.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.spring5.junit4; 2 | 3 | import com.dmitrijdrandarov.spring5.Spring5Config; 4 | import com.dmitrijdrandarov.entities.DummyFruit; 5 | import com.dmitrijdrandarov.spring5.repositories.DummyFruitRepository; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.test.context.ContextConfiguration; 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import java.util.List; 17 | 18 | @ContextConfiguration(classes = {Spring5Config.class}) 19 | @RunWith(SpringRunner.class) 20 | @Transactional 21 | public class Spring5_01_JUnit4Test { 22 | 23 | private static final Logger LOG = LoggerFactory.getLogger(Spring5_01_JUnit4Test.class); 24 | 25 | @Autowired 26 | private DummyFruitRepository fruitRepository; 27 | 28 | @Test 29 | public void saveTest() { 30 | DummyFruit fruity = fruitRepository.save(new DummyFruit(null, DummyFruit.TYPE.APPLE, "Fruity", "Description", 10D)); 31 | fruity = fruitRepository.findOneById(fruity.getId()); 32 | Assert.assertNotNull(fruity); 33 | 34 | // LOG.info(fruity.toString()); 35 | System.out.println(fruity.toString()); 36 | } 37 | 38 | @Test 39 | public void dataSQLTest() { 40 | List dummyFruits = fruitRepository.findAllBy(); 41 | Assert.assertEquals(3, dummyFruits.size()); 42 | 43 | // dummyFruits.stream().map(DummyFruit::toString).forEach(LOG::info); 44 | dummyFruits.stream().map(DummyFruit::toString).forEach(System.out::println); 45 | } 46 | } -------------------------------------------------------------------------------- /03-spring/03-04-spring-5/src/test/java/com/dmitrijdrandarov/spring5/junit5/Spring5_02_JUnit5Test.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.spring5.junit5; 2 | 3 | import com.dmitrijdrandarov.spring5.Spring5Config; 4 | import com.dmitrijdrandarov.entities.DummyFruit; 5 | import com.dmitrijdrandarov.spring5.repositories.DummyFruitRepository; 6 | import org.junit.jupiter.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.test.context.ContextConfiguration; 13 | import org.springframework.test.context.junit.jupiter.SpringExtension; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | import java.util.List; 17 | 18 | @ContextConfiguration(classes = {Spring5Config.class}) 19 | @ExtendWith(SpringExtension.class) 20 | @Transactional 21 | class Spring5_02_JUnit5Test { 22 | 23 | private static final Logger LOG = LoggerFactory.getLogger(Spring5_02_JUnit5Test.class); 24 | 25 | @Autowired 26 | private DummyFruitRepository fruitRepository; 27 | 28 | @Test 29 | void saveTest() { 30 | DummyFruit fruity = fruitRepository.save(new DummyFruit(null, DummyFruit.TYPE.APPLE, "Fruity", "Description", 10D)); 31 | fruity = fruitRepository.findOneById(fruity.getId()); 32 | Assertions.assertNotNull(fruity); 33 | 34 | // LOG.info(fruity.toString()); 35 | System.out.println(fruity.toString()); 36 | } 37 | 38 | @Test 39 | void dataSQLTest() { 40 | List dummyFruits = fruitRepository.findAllBy(); 41 | Assertions.assertEquals(3, dummyFruits.size()); 42 | 43 | // dummyFruits.stream().map(DummyFruit::toString).forEach(LOG::info); 44 | dummyFruits.stream().map(DummyFruit::toString).forEach(System.out::println); 45 | } 46 | } -------------------------------------------------------------------------------- /03-spring/03-04-spring-5/src/test/java/com/dmitrijdrandarov/spring5/repositories/DummyFruitRepository.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.spring5.repositories; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface DummyFruitRepository extends CrudRepository { 9 | 10 | List findAllBy(); 11 | DummyFruit findOneById(Long id); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /03-spring/03-04-spring-5/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO DummyFruit (name, description, value, type) VALUES 2 | ('Baby Banana', 'It is yellow!', 20.0, 'BANANA'), 3 | ('Granny Smith Apple', 'Delicious!', 10.5, 'APPLE'), 4 | ('Grapefruit', 'It is totally an orange, baka!', 8.5, 'ORANGE'); -------------------------------------------------------------------------------- /03-spring/03-04-spring-5/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE DummyFruit ( 2 | id INTEGER IDENTITY PRIMARY KEY, 3 | name VARCHAR(256), 4 | description VARCHAR(256), 5 | value DOUBLE, 6 | type VARCHAR(256) 7 | ); -------------------------------------------------------------------------------- /04-testfx/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | 5 | 6 | // Commons 7 | testCompile project(":common") 8 | 9 | 10 | // Logging 11 | testCompile "org.slf4j:slf4j-simple:$slf4j_version" 12 | } -------------------------------------------------------------------------------- /05-mockito/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Main-dependency needed for JUnit 5 3 | testCompile "org.junit.jupiter:junit-jupiter-api:$junit5_version" 4 | 5 | 6 | // Mockito 7 | testCompile "org.mockito:mockito-core:$mockito_version" 8 | 9 | 10 | // Commons 11 | testCompile project(":common") 12 | 13 | 14 | // Logging 15 | testCompile "org.slf4j:slf4j-simple:$slf4j_version" 16 | 17 | } 18 | -------------------------------------------------------------------------------- /05-mockito/src/test/java/com.dmitrijdrandarov/mockito/Mockito_Basics.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.mockito; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Disabled; 6 | import org.junit.jupiter.api.Test; 7 | import org.mockito.Mockito; 8 | 9 | /** 10 | * TODO 11 | */ 12 | class Mockito_Basics { 13 | 14 | @Test 15 | @Disabled("Incompatible with JDK 12") 16 | void basic() { 17 | DummyFruit redBanana = Mockito.mock(DummyFruit.class); 18 | redBanana.setName("Red Banana"); 19 | redBanana.setType(DummyFruit.TYPE.BANANA); 20 | 21 | Mockito.when(redBanana.getType()).thenReturn(DummyFruit.TYPE.APPLE); 22 | 23 | Assertions.assertEquals(DummyFruit.TYPE.APPLE, redBanana.getType()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /JUG - Modern Testing mit JUnit 5 - Light.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/JUG - Modern Testing mit JUnit 5 - Light.pptx -------------------------------------------------------------------------------- /JUG - Modern Testing mit JUnit 5.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/JUG - Modern Testing mit JUnit 5.pptx -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Dmitrij Drandarov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Modern Testing with JUnit 5, AssertJ, Spring, TestFX and Mockito 2 | Dmitrij Drandarov 3 | :imagesdir: img 4 | :icons: font 5 | :toc: 6 | 7 | == Authors 8 | 9 | * Dmitrij Drandarov | link:https://github.com/dmitrij-drandarov[image:github.png[20px, 20px]] | link:https://twitter.com/drandarov_io[image:twitter.png[20px, 20px]] | link:https://www.xing.com/profile/Dmitrij_Drandarov[image:xing.png[20px, 20px]] | link:https://www.linkedin.com/in/dmitrij-drandarov/[image:linkedin.png[20px, 20px]] 10 | 11 | == Repository Introduction 12 | 13 | This repository contains several modules focused on showing different frameworks and tools in conjunction with JUnit 5. 14 | These are however only presented as code since the main focus of this repository is the introduction to JUnit 5. 15 | 16 | .**More practices, frameworks and tools in conjunction with JUnit 5** 17 | * link:00-junit-best-practice/src/test/java/com.dmitrijdrandarov/bestpractice[**Best practices**] 18 | * link:02-assertj/src/test/java/com.dmitrijdrandarov/assertj[**AssertJ**] 19 | * link:03-spring[**Spring**] 20 | * link:04-testfx/src/test/java/com.dmitrijdrandarov/testfx[**TestFX (ToDo)**] 21 | * link:05-mockito/src/test/java/com.dmitrijdrandarov/mockito[**Mockito (WIP)**] 22 | 23 | == Slides from Talk 24 | 25 | image::05_jug_final_small.png[] 26 | 27 | * Light: link:++JUG - Modern Testing mit JUnit 5 - Light.pptx++[] 28 | * Dark: link:++JUG - Modern Testing mit JUnit 5.pptx++[] 29 | 30 | = JUnit 5 Quick-Start-Guide 31 | 32 | :sectnums: 33 | :sectlinks: 34 | 35 | Testing is important. 36 | We should all know that. 37 | They can help reproduce and fix problems in our code. 38 | They can also remind you that you shouldn't have done something long after you forgot that you shouldn't do that thing and a lot more. 39 | 40 | The problem is that often developers or managers see testing as some overhead for development that should be done after the code is finished if at all. 41 | That's wrong: Testing should be a vital part of development! 42 | That problem is something JUnit 5 can help you with. 43 | It introduced a lot of interesting, fast ways to create tests. 44 | Be it Test-Factories, Test-Extensions (that only have to be implemented once), Lambda-Support etc. 45 | It pushes the D.R.Y. principle a lot! 46 | 47 | To follow this guide you should know the basics of testing and JUnit (4), but otherwise you wouldn't be here I guess. 48 | 49 | Some headers related to code will have a code-link behind their name directing to the corresponding class in the GitHub-Repository. 50 | 51 | == Setup 52 | 53 | .**Maven** 54 | [source,xml] 55 | ---- 56 | 57 | org.junit.jupiter 58 | junit-jupiter-api 59 | 5.0.3 60 | test 61 | 62 | ---- 63 | If you need lagacy support for JUnit 4 or even JUnit 3 to run in parallel to JUnit 5, you can add the following dependency with the right version to your project 64 | [source,xml] 65 | ---- 66 | 67 | org.junit.vintage 68 | junit-vintage-engine 69 | 4.12.0 70 | test 71 | 72 | ---- 73 | 74 | .**Equivalents for Gradle** 75 | [source,gradle] 76 | ---- 77 | testCompile 'org.junit.jupiter:junit-jupiter-api:5.0.3' 78 | ---- 79 | [source,gradle] 80 | ---- 81 | testCompile 'org.junit.vintage:junit-vintage-engine:4.12.0' 82 | ---- 83 | 84 | 85 | == General changes link:01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_01_GeneralChanges.java[(code)] 86 | 87 | 88 | === Syntax 89 | This paragraph contains the small or general changes made in the transition from JUnit 4 to JUnit 5. 90 | Those are simple but still note worthy. 91 | 92 | The first change is made to the most basic of things: the test and the `@Test`-annotation themselves. 93 | You no longer need to make the test `public`, however you still must not make it `static` or `private`. 94 | Also timeout and expected parameter functionality has moved elsewhere. 95 | 96 | [source,java] 97 | ---- 98 | /** 99 | * Tests now only must not be static or private. Latter also goes for @Before.../@After... 100 | * timeout = ? and expected = ? functionality has now moved elsewhere. See in {@link JUnit5_01_NewFeaturesBasics} 101 | */ 102 | @Test 103 | void testTest() {} 104 | ---- 105 | 106 | === Naming 107 | 108 | Other annotations have received slight changes as well, including the common `@BeforeClass`, `@BeforeEach`, their `@After...` equivalents, `@Ignored` and the lesser known `@Category`. 109 | All of these have been renamed and given the same treatment regarding `public` as `@Test`. 110 | 111 | [source,java] 112 | ---- 113 | /** 114 | * Annotation @BeforeClass was replaced by @{@link BeforeAll}. Needs to be static. 115 | * Same for @AfterClass. 116 | */ 117 | @BeforeAll 118 | static void beforeAll() {} 119 | 120 | /** 121 | * Annotation @Before was replaced by @{@link BeforeEach}. 122 | * Same for @After. 123 | */ 124 | @BeforeEach 125 | void beforeEach() {} 126 | 127 | /** 128 | * Annotation @Ignore was replaced by @{@link Disabled}. Sounds less negative. 129 | * However a reason for the deactivation will be printed which can be more advanced with features like {@link ExecutionCondition}. 130 | */ 131 | @Disabled 132 | @Test 133 | void disabledTest() {} 134 | 135 | /** 136 | * JUnit 4s experimental @Category is now called {@link Tag}/{@link Tags}. 137 | */ 138 | @Tag("abc") 139 | @Test 140 | void taggedTest() {} 141 | ---- 142 | 143 | `Assert` and `Assume` classes have been renamed as well and are now called `Assertions` and `Assumptions`. 144 | Not much has changed for the naming of the methods of both classes. 145 | 146 | [source,java] 147 | ---- 148 | /** 149 | * Assertion Methods are now in class {@link Assertions}. Method names stayed mostly the same otherwise. 150 | */ 151 | @Test 152 | void assertionsTest() { 153 | Assertions.assertTrue(true); // Without static import 154 | assertTrue(true); // With static import on org.junit.jupiter.api.Assertions.assertTrue() 155 | } 156 | 157 | /** 158 | * Assumption Methods are now in class {@link Assumptions}. Method names stayed mostly the same otherwise. 159 | */ 160 | @Test 161 | void assumptionsTest() { 162 | Assumptions.assumeTrue(true); // Without static import 163 | assumeTrue(true); // With static import on org.junit.jupiter.api.Assumptions.assumeTrue() 164 | } 165 | ---- 166 | 167 | 168 | == New features: Basics link:01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_02_NewFeaturesBasics.java[(code)] 169 | 170 | === General 171 | Here I want to introduce some basics for the new features available in JUnit 5. 172 | There is a new annotation called `@DisplayName` which is supposed to improve the readability of test reports, so you don't need 50-character test method names to make clear what the test is about at a glance. 173 | 174 | [source,java] 175 | ---- 176 | /** 177 | * Tests can now receive Display-Names via @{@link DisplayName}. These are e.g. used by the IDE, Console or the 178 | * {@link TestInfo}-Parameter (addressed in {@link #parameterTest(TestInfo, TestReporter)}). 179 | */ 180 | @Test 181 | @DisplayName("Choose a display name") 182 | void displayNameTest() {} 183 | ---- 184 | 185 | image::01_displayname_result.png[] 186 | 187 | You can now also group tests with inner classes annotated with `@Nested`. 188 | 189 | [source,java] 190 | ---- 191 | @Nested 192 | @DisplayName("Tests grouped by something") 193 | class groupedTests { 194 | 195 | @Test 196 | void groupedTest1() {} 197 | 198 | @Test 199 | void groupedTest2() {} 200 | 201 | } 202 | ---- 203 | 204 | image::02_nestedTests_result.png[] 205 | 206 | === Assertions and Lambda-Support 207 | Now for the probably most known and anticipated feature in JUnit 5: Lambda-Support... 208 | JUnit 5 `Assertions` and `Assumptions` classes and its methods now provide Lambda support. 209 | This is achieved by providing methods with functional interfaces as parameters. 210 | 211 | The most used ones are the `BooleanSupplier` and `Supplier`. 212 | The first one is used for assertions and the latter one to provide a result-message. 213 | Those are however just alternatives to the older plain `boolean` and `String`. 214 | Assertion methods like `assertTrue(...)` are now overloaded with combinations of those four parameters: 215 | (`boolean` | `BooleanSupplier`) & (`String` | `Supplier`) resulting in 4 different methods. 216 | This is what most lambda-supporting methods are designed like. 217 | 218 | [source,java] 219 | ---- 220 | /** 221 | * The new assertion-methods now support supplier-interfaces, meaning you can now enter lambda expressions on the 222 | * fly to a lot of the assert-methods. 223 | * E.g. by giving a {@link BooleanSupplier} for the assertion and a ({@link Supplier} for the 224 | * result-message to the {@link Assertions#assertTrue(BooleanSupplier, Supplier)} method. 225 | */ 226 | @Test 227 | void assertLambdaTest() { 228 | assertTrue(() -> Boolean.parseBoolean("true")); // Simple assertTrue() with BooleanSupplier-Lambda-Implement. 229 | Assertions.assertTrue(true, this.getClass()::getName); // Method references are possible as well of course 230 | } 231 | ---- 232 | 233 | A new important functional interface is `Executable`. 234 | It is very similar to a `Runnable`, however it throws a `Throwable` meaning you can execute assertions like `assertTrue()` and an `AssertionError` may be thrown affecting your test-result. 235 | It is used in several assertions like the new `assertAll(Executable... executables)` which can be also used to prevent repetition. 236 | 237 | [source,java] 238 | ---- 239 | /** 240 | * {@link Assertions} has a method called {@link Assertions#assertAll(Executable...)} that enables us to group 241 | * assertions, as well as reuse them. 242 | */ 243 | @Test 244 | void assertAllTest() { 245 | Executable[] executables = { 246 | () -> assertTrue(getData() >= -10), 247 | () -> assertTrue(getData() <= +15)}; 248 | 249 | Assertions.assertAll("Random Tests", executables); 250 | dataChanges(); 251 | Assertions.assertAll("Random Tests Again", executables); 252 | } 253 | ---- 254 | 255 | This new functional interface is also used in the new replacement of the old `@Test`-parameter `expected` which is called `assertThrows()`. 256 | It asserts whether an exception was thrown. 257 | If you need the exception-instance itself to e.g. assert the message, you can instead use `expectThrows()` which also has the exception as return type. 258 | 259 | [source,java] 260 | ---- 261 | /** 262 | * The expected parameter of {@link Test} has moved to {@link Assertions#assertThrows(Class, Executable)}. 263 | */ 264 | @Test 265 | void assertThrowsTest() { 266 | assertThrows(ArrayIndexOutOfBoundsException.class, 267 | () -> (new String[1])[2] = "I will throw an Exception :)"); 268 | } 269 | ---- 270 | 271 | [source,java] 272 | ---- 273 | /** 274 | * You can also use {@link Assertions#assertThrows(Class, Executable)} to get the {@link Exception}-Instance if you need it. 275 | */ 276 | @Test 277 | void expectThrowsTest() { 278 | ArrayIndexOutOfBoundsException exc = assertThrows(ArrayIndexOutOfBoundsException.class, 279 | () -> (new String[1])[2] = "I will throw an Exception :)"); 280 | 281 | assertEquals(exc.getMessage(), "2"); 282 | } 283 | ---- 284 | 285 | === Parameter Resolver 286 | 287 | The biggest new feature in JUnit 5 is the new Extension-API. 288 | A part of it is the `ParameterResolver`-Interface which is an extension of the `Extension`-Interface itself. 289 | The `ParameterResolver`-Interface provide a way for dependency injection on method level by injecting data into test-method parameters. 290 | JUnit 5 provides two implementations by itself: `TestInfo` which contains some meta information and the appropriate Test-`Method` and Test-`Class` instances and `TestReporter` which can be used to publish test entries. 291 | A lot more on the Extension-Api is following further below. 292 | 293 | [source,java] 294 | ---- 295 | /** 296 | * Tests can now be provided with parameters. Those are resolved by {@link ParameterResolver}-Implementations which 297 | * in turn are extensions of the above mentioned {@link Extension}. 298 | * This enables dependency injection at method level. 299 | * 300 | * Resolvers for {@link TestInfo} and {@link TestReporter} are already provided. Other parameters require your own 301 | * {@link ParameterResolver}-Implementations to be added with the @{@link ExtendWith}-Annotation to either the 302 | * class or method. 303 | * 304 | * @param testInfo Information about the current test 305 | * @param testReporter Used to publish test entries 306 | */ 307 | @Test 308 | void parameterTest(TestInfo testInfo, TestReporter testReporter) { 309 | LOG.info("DisplayName:\t" + testInfo.getDisplayName()); 310 | LOG.info("Tags:\t\t\t" + testInfo.getTags()); 311 | LOG.info("TestClass:\t\t" + testInfo.getTestClass()); 312 | LOG.info("TestMethod:\t\t" + testInfo.getTestMethod()); 313 | 314 | testReporter.publishEntry("parameterTestTime", String.valueOf(System.currentTimeMillis())); 315 | } 316 | ---- 317 | 318 | 319 | == New features: Advanced link:01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_03_NewFeaturesAdvanced.java[(code)] 320 | 321 | === Test-Parameters 322 | Building upon the `ParameterResolver` paragraph of the last chapter let's look at implementing your own `ParameterResolver`. 323 | You can also see the first visual sign of the Extension-API in the form of the `@ExtendWith`-Annotation. The final result is: 324 | 325 | [source,java] 326 | ---- 327 | /** 328 | * A simple example of a {@link ParameterResolver}-Implementation. @{@link ExtendWith} is used to mark 329 | * {@link ClassName_ParameterResolver} and {@link ParameterIndex_ParameterResolver} as used 330 | * {@link ParameterResolver}. These could alternatively be placed at class level. 331 | * 332 | * @param className String-Parameter that will be injected by {@link ClassName_ParameterResolver} 333 | * @param parameterIndex Long-Parameter that will be injected by {@link ParameterIndex_ParameterResolver} 334 | */ 335 | @Test 336 | @ExtendWith({ClassName_ParameterResolver.class, ParameterIndex_ParameterResolver.class}) 337 | void customParameterTest(String className, Long parameterIndex) { 338 | LOG.info(className); // Surrounding class name injected by ClassName_ParameterResolver 339 | LOG.info(parameterIndex); // Parameter-Index injected by ParameterIndex_ParameterResolver 340 | } 341 | ---- 342 | 343 | This is achieved by the following implementations: 344 | 345 | The first implementation processes the `String` parameter `className`. 346 | It checks whether the parameter class is a `String` and throws an exception otherwise. 347 | To resolve and inject the parameter it just returns the test classes name. 348 | 349 | [source,java] 350 | ---- 351 | public class ClassName_ParameterResolver implements ParameterResolver { 352 | 353 | /** 354 | * Simple example that only checks if the Parameter-Type is a {@link String} based on the Parameter-Context to 355 | * determine whether the Parameter is supported by this {@link ParameterResolver}. 356 | */ 357 | @Override 358 | public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 359 | return parameterContext.getParameter().getType().equals(String.class); 360 | } 361 | 362 | /** 363 | * Simple example that simply resolves the Parameter by returning the Class-Name based on the Parameter-Context. 364 | */ 365 | @Override 366 | public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 367 | Class contextClass = extensionContext.getTestClass().orElse(null); 368 | 369 | return contextClass == null ? null : contextClass.getSimpleName(); 370 | } 371 | 372 | } 373 | ---- 374 | 375 | The seconds implementation processes the `Long` parameter `parameterIndex`. 376 | It does basically the same but resolves the parameter by getting the index from the `parameterContext`. 377 | 378 | [source,java] 379 | ---- 380 | public class ParameterIndex_ParameterResolver implements ParameterResolver { 381 | 382 | /** 383 | * Simple example that only checks if the Parameter-Type is a {@link Long} based on the 384 | * Parameter-Context to determine whether the Parameter is supported by this 385 | * {@link ParameterResolver}. 386 | */ 387 | @Override 388 | public boolean supports(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 389 | return parameterContext.getParameter().getType().equals(Long.class); 390 | } 391 | 392 | /** 393 | * Simple example that simply resolves the Parameter by returning the parameterIndex based 394 | * on the Parameter-Context. 395 | */ 396 | @Override 397 | public Object resolve(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { 398 | return (long) parameterContext.getIndex(); 399 | } 400 | 401 | } 402 | ---- 403 | 404 | Fancier examples will be down below. 405 | 406 | === Test-Factories 407 | Another nice feature are the new Test-Factories. 408 | These are annotated with `@TestFactory` instead of `@Test`. 409 | Their return type is some kind of collection of ``DynamicTest``s. 410 | The class `DynamicTest` provides several static methods to create those. 411 | You basically have to provide test data and based on it a display name as well as some kind of `Executable`. 412 | In my example you can see me using the `stream()`-method of said class. 413 | 414 | [source,java] 415 | ---- 416 | /** 417 | * An example for a {@link TestFactory} with JUnit 5. 418 | * {@link DynamicTest#stream(Iterator, Function, ThrowingConsumer)} provides an easy way to factorize multiple 419 | * tests, which will be executed automatically. 420 | * It's basically similar to a for-loop that reads data and asserts, but these test will be grouped and displayed 421 | * separately in the test results. 422 | * 423 | * @return A stream of dynamic tests 424 | */ 425 | @TestFactory 426 | Stream testStreamFactoryTest() { 427 | Iterator testData = Arrays.asList(new String[]{"1", "2", "3"}).iterator(); 428 | 429 | return DynamicTest.stream( 430 | testData, // Input-Data for the Factory 431 | s -> "Displayname: S" + s, // Creating DisplayNames for the test 432 | Assertions::assertNotNull); // Providing an Executable on which the test is based 433 | } 434 | ---- 435 | 436 | image::03_testFactory_result.png[] 437 | 438 | === Test-Extensions 439 | Here I will show you an `Extension` that is not based on the `ParameterResolver` but instead implements the `ExecutionCondition`. 440 | The same thing that powers the `@Disabled` annotation. 441 | If we want to customize it we need out own implementation. 442 | There are about a dozen of those `Extension` categories. 443 | `ExecutionCondition` is just one of them. 444 | Some are functional interfaces like the one we're talking about, others like the `ParameterResolver` are not. 445 | 446 | My example called `@DisabledOnMonday` does exactly what it says it does: 447 | It disables that test method or class on Mondays. 448 | The implementation only checks for the weekday and returns an appropriate `ConditionEvaluationResult` resulting in the test being ignored when the weekday matches. 449 | 450 | [source,java] 451 | ---- 452 | /** 453 | * An extension that disables a test class on Mondays, because nobody likes those, right? 454 | * 455 | * @author dmitrij-drandarov 456 | * @since 28 Jul 2016 457 | */ 458 | public class DisabledOnMonday implements ExecutionCondition { 459 | 460 | @Override 461 | public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { 462 | boolean monday = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY; 463 | 464 | return monday ? 465 | ConditionEvaluationResult.disabled("I spare you on Mondays.") : 466 | ConditionEvaluationResult.enabled("Don't spare you on other days though >:("); 467 | } 468 | 469 | } 470 | ---- 471 | 472 | The test method looks like this: 473 | 474 | [source,java] 475 | ---- 476 | /** 477 | * For this example I use my implementation of {@link ExecutionCondition} called {@link DisabledOnMonday} to 478 | * tell JUnit to disable this test on mondays, because who likes those, right? 479 | * 480 | * This annotation might just as well be placed on class level. To see how I implemented this look at 481 | * {@link DisabledOnMonday}. 482 | */ 483 | @Test 484 | @ExtendWith(DisabledOnMonday.class) 485 | void disabledOnMondayTest() {} 486 | ---- 487 | 488 | Again: This could without problem be placed on class-level. 489 | 490 | 491 | == Advanced Examples link:01-junit-5/src/test/java/com.dmitrijdrandarov/junit5/JUnit5_04_AdvancedExamples.java[(code)] 492 | 493 | === Extended disabled weekdays 494 | Let's extend that `@DisabledOnMonday` annotation a bit. 495 | What if you want to choose the weekday? 496 | Creating 7 annotations is kind of overkill. 497 | A way to achieve this could be to add another annotation that accepts the weekdays as a parameter: 498 | 499 | [source,java] 500 | ---- 501 | /** 502 | * Here I go a step further and annotate my days dynamically, by specifying the days I don't want the test to run 503 | * on with another custom annotation called @{@link DisabledWeekdays}. 504 | * 505 | * My extension {@link DisabledOnWeekday} later searches for @{@link DisabledWeekdays} and determines whether the 506 | * test should run or not. 507 | */ 508 | @Test 509 | @DisabledWeekdays({Calendar.THURSDAY, Calendar.SATURDAY}) 510 | @ExtendWith(DisabledOnWeekday.class) 511 | void disabledOnWeekdaysTest() {} 512 | ---- 513 | 514 | The `@DisabledWeekdays` annotation doesn't do much more than hold an int array corresponding to the weekdays. 515 | 516 | [source,java] 517 | ---- 518 | /** 519 | * A simple annotation to retain information about weekdays that the annotated tests are disabled on. 520 | * Used by {@link DisabledOnWeekday}-Extension. 521 | * 522 | * @author dmitrij-drandarov 523 | * @since 28 Jul 2016 524 | */ 525 | @Retention(RetentionPolicy.RUNTIME) 526 | public @interface DisabledWeekdays { 527 | int[] value(); 528 | } 529 | ---- 530 | 531 | The extension looks slightly different now, since it needs to determine the weekdays from the annotation. 532 | Luckily the `evaluateExecutionCondition()`-method provides the `ExtensionContext` so it's fairly easy to get those. 533 | 534 | [source,java] 535 | ---- 536 | /** 537 | * An extension that disables this test class on the weekday specified by {@link DisabledWeekdays}. 538 | * 539 | * @author dmitrij-drandarov 540 | * @since 28 Jul 2016 541 | */ 542 | public class DisabledOnWeekday implements ExecutionCondition { 543 | 544 | @Override 545 | public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { 546 | 547 | // Search for the @DisabledWeekdays annotation from the TestExtensionContext 548 | Optional contextElement = context.getElement(); 549 | AnnotatedElement annotatedElement = contextElement.orElse(null); 550 | 551 | if (annotatedElement == null) return null; 552 | 553 | DisabledWeekdays weekdayAnnotation = annotatedElement.getAnnotation(DisabledWeekdays.class); 554 | 555 | // Determine whether the test should be disabled 556 | boolean weekdayToday = IntStream.of(weekdayAnnotation.value()) 557 | .anyMatch(day -> day == Calendar.getInstance().get(Calendar.DAY_OF_WEEK)); 558 | 559 | // Return a ConditionEvaluationResult based on the outcome of the boolean weekdayToday 560 | return weekdayToday ? 561 | ConditionEvaluationResult.disabled("I spare you today.") : 562 | ConditionEvaluationResult.enabled("Don't spare you on other days though >:("); 563 | } 564 | 565 | } 566 | ---- 567 | 568 | === Extend Test-Annotation 569 | So what if you want to save some that space occupied by all those annotations. 570 | Let's make it all-in-one for this example: 571 | 572 | [source,java] 573 | ---- 574 | /** 575 | * Here I use an annotation @{@link UITest} that is annotated by @{@link Test} itself, so it will be executed 576 | * properly. @{@link UITest} contains grouped information and annotations about this test like predefined 577 | * extensions. Further information in @{@link UITest}s JavaDoc. 578 | *# 579 | * This of course could be also possible for the examples above. 580 | */ 581 | @UITest("../../sample.fxml") 582 | void userInterfaceTest(Pane root) { 583 | LOG.info(root.getPrefWidth()); // 555.0 (defined in FXML-File) 584 | LOG.info(root.getPrefHeight()); // 333.0 (defined in FXML-File) 585 | } 586 | ---- 587 | 588 | What you basically do here is to create a new annotation and annotate that with `@Test`. 589 | Then you pack all you need in there like your extensions, parameter resolvers, targets, parameters, etc. 590 | The annotation `@UITest` above looks like this: 591 | 592 | [source,java] 593 | ---- 594 | /** 595 | * Test annotated by this will be executed by the test runner without problems due to @{@link Test} being included. 596 | * You can basically group annotations by doing this and save some space, by not having to add all those 597 | * {@link ExtendWith}s etc. to each method. 598 | * Readability inside the test classes is the key here. And it looks cooler ;) 599 | * 600 | * @author dmitrij-drandarov 601 | * @since 29 Jul 2016 602 | */ 603 | @Test 604 | @Tag("userInterface") // For simple identification by ParameterResolvers 605 | @ExtendWith(PrintUITestData.class) // Prints UI Test Data before each test 606 | @ExtendWith(RootElementResolver.class) // Resolves the root pane 607 | @Target(ElementType.METHOD) 608 | @Retention(RetentionPolicy.RUNTIME) // Required for the test to be automatically executed 609 | public @interface UITest { 610 | 611 | /** 612 | * FXML-Path. 613 | * 614 | * @return FXML-Path used for the UI-Test. 615 | */ 616 | String value(); 617 | 618 | } 619 | ---- 620 | 621 | The extensions used do not really matter here. 622 | One extension resolves the `Pane` from the fxml path and the other one just prints some data. 623 | This is rather a showcase of an `@Test`-Extension and utilizing the extension features of JUnit 5. 624 | If you want to see code nevertheless look into the repository. 625 | 626 | === Benchmarking Example 627 | 628 | As for the last example right now I will showcase some benchmarking possibilities and it isn't even that complicated. 629 | There are several extensions that can be used for that. 630 | `BeforeAllCallback`, `BeforeTestExecutionCallback` and their `After...`-equivalents. 631 | Each of these interfaces has a method that will be executed at some point during the tests. 632 | E.g. before each test or after etc. 633 | So by implementing those 4 interfaces in one extension we can create a class that timestamps each time a method is called and after it finished including calculating the difference. 634 | Then we just need to annotate an annotation `@Benchmarked` with that extension and then place that on top of a test-method or -class. 635 | Done! 636 | The final benchmarked test-method will should something like this: 637 | 638 | [NOTE] 639 | ==== 640 | The implementation is just for showcase. It isn't very accurate or performant. 641 | ==== 642 | 643 | [source,java] 644 | ---- 645 | /** 646 | * For this example I wrote an annotation @{@link Benchmarked} that doesn't include @{@link Test} - which it could - 647 | * but instead only contains an self-written extension called {@link BenchmarkExtension}. Annotating your class 648 | * with this will basically provide you with automatic benchmarking. 649 | * 650 | * This could of course be also placed on top of the class. 651 | */ 652 | @Test 653 | @Benchmarked 654 | void benchmarkedTest() { 655 | LOG.info("Calculating some primes..."); 656 | int primeCount = 200000; 657 | 658 | assertEquals(primeCount, IntStream.iterate(2, i -> i + 1) 659 | .filter(i -> LongStream.rangeClosed(2, (long) (Math.sqrt(i))).allMatch(n -> i % n != 0)) 660 | .limit(primeCount).toArray().length); 661 | } 662 | ---- 663 | 664 | The corresponding test-output: 665 | 666 | image::04_benchmarked_output.png[] 667 | 668 | The extension couldn't be simpler: 669 | 670 | [source,java] 671 | ---- 672 | /** 673 | * Extension, that does the logging for the benchmarks. (Implementation is not accurate or performant!) 674 | * 675 | * @author dmitrij-drandarov 676 | * @since 29 Jul 2016 677 | */ 678 | public class BenchmarkExtension implements BeforeAllCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, AfterAllCallback { 679 | 680 | private static final String APD = "\t-\t"; 681 | 682 | private static final Map startTime = new HashMap<>(); 683 | private static final DateFormat dtForm = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); 684 | 685 | @Override 686 | public void beforeAll(ExtensionContext context) throws Exception { 687 | String disp = context.getDisplayName(); 688 | long start = currentTimeMillis(); 689 | 690 | LOG.info("#### Summary \t" + APD + disp + " ####"); 691 | LOG.info("#### Start of Benchmark\t" + APD + disp + APD + dtForm.format(new Date(start)) + " ####"); 692 | startTime.put(disp, start); 693 | } 694 | 695 | @Override 696 | public void beforeTestExecution(ExtensionContext context) throws Exception { 697 | String disp = context.getDisplayName(); 698 | long start = currentTimeMillis(); 699 | 700 | LOG.info("#### Method-Benchm. ####" + APD + disp + APD + dtForm.format(new Date(start))); 701 | startTime.put(context.getDisplayName(), start); 702 | } 703 | 704 | @Override 705 | public void afterTestExecution(ExtensionContext context) throws Exception { 706 | String disp = context.getDisplayName(); 707 | long end = currentTimeMillis(); 708 | 709 | LOG.info("#### Summary ####" + APD + disp); 710 | LOG.info("#### Start ####" + APD + dtForm.format(new Date(startTime.get(disp)))); 711 | LOG.info("#### End ####" + APD + dtForm.format(new Date(end))); 712 | LOG.info("#### Duration ####" + APD + (end - startTime.get(disp)) + " ms\n"); 713 | } 714 | 715 | @Override 716 | public void afterAll(ExtensionContext context) throws Exception { 717 | String disp = context.getDisplayName(); 718 | long end = currentTimeMillis(); 719 | 720 | LOG.info("#### End of Benchmark \t" + APD + disp + APD + dtForm.format(new Date(end)) + " ####"); 721 | LOG.info("#### Duration for class\t" + APD + disp + APD + (end - startTime.get(disp)) + " ms ####"); 722 | } 723 | 724 | } 725 | ---- 726 | 727 | Of course I could have also included `@Benchmarked` in a separate `@BenchmarkedTest` annotation that would have extended `@Test` as well saving that one line. 728 | 729 | 730 | == Closing words 731 | 732 | === Contribution 733 | Feel free to express critique and contribute to the https://github.com/msg-DAVID-GmbH/JUnit-5-QuickStart-Guide-and-Advanced[repository] :) 734 | 735 | === Usage 736 | You can use this repository in any way you want. 737 | May it be for workshops or presentations. Just give credits. ;) 738 | 739 | === Further Reference 740 | [%hardbreaks] 741 | * http://junit.org/junit5/docs/current/user-guide[Official JUnit 5 User Guide] 742 | * https://github.com/junit-team/junit5[JUnit 5 GitHub] 743 | * https://github.com/junit-team/junit5/milestones/[JUnit 5 Milestone plan] 744 | * http://files.zeroturnaround.com/pdf/zt_junit_cheat_sheet.pdf[Cheat Sheet from ZERO TURNAROUND] 745 | 746 | == Repository Timeline 747 | 748 | * Everything else is now tracked as issues 749 | * [x] Update for M4-RC2 - 07 Sep 2017 750 | * [x] Fix userInterfaceTest - 01 Aug 2017 751 | * [x] Create wiki article with githup-pages-content - 04 Mar 2017 752 | * [x] Convert code fragments from images to text - 04 Mar 2017 753 | * [x] Incorporate JUnit Best-Practice on request - 15 Feb 2017 754 | * [x] Change name - 05 Aug 2016 755 | * [x] Finish Stream TODOs - 05 Aug 2016 756 | * [x] Proper Presentation - 04 Aug 2016 757 | * [x] Add reference - 03 Aug 2016 758 | * [x] Add expectThrows() - 03 Aug 2016 759 | * [x] Add @Nested - 03 Aug 2016 760 | * [x] Adjust packages and classes for presentation - 01 Aug 2016 761 | * [x] Extend Test-Extensions - 29 Jul 2016 762 | * [x] Test-Extensions (o\j\j\api\extension) - 28 Jul 2016 763 | * [x] @TestFactory + DynamicTests - 26 Jul 2016 764 | * [x] Reorder packages and classes - 26 Jul 2016 765 | * [x] Links to Java-Files - 24 Jul 2016 766 | * [x] Dependency Copy-Paste Resource - 24 Jul 2016 767 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | 3 | plugins: 4 | - jekyll-asciidoc 5 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.dmitrij-drandarov' 2 | version '5.0' 3 | 4 | ext { 5 | // JUnit 5 6 | junit5_version = '5.4.0' 7 | junit5_vintage4_version = '5.4.0' 8 | junit5_spring4_test_version = '1.0.0' 9 | junit4_version = '4.12' 10 | 11 | // Spring 5 12 | spring5_version = '5.1.1.RELEASE' 13 | spring5_data_jpa_version = '2.1.1.RELEASE' 14 | // Spring 5 Boot 15 | spring5_boot_version = '2.0.6.RELEASE' 16 | 17 | // Spring 4 18 | spring4_version = '1.11.7.RELEASE' 19 | // Spring 4 Boot 20 | spring4_boot_version = '1.5.7.RELEASE' 21 | 22 | // OpenJFX 23 | openjfx_version = "11.0.0-ea1" 24 | 25 | // Hibernate 26 | hibernate_core_version = '5.2.10.Final' 27 | hibernate_jpa_version = '1.0.0.Final' 28 | 29 | // Assertion 30 | hamcrest_version = '1.3' 31 | assertj_version = '3.8.0' 32 | 33 | // Mockito 34 | mockito_version = '2.9.0' 35 | 36 | // Logging 37 | slf4j_version = '1.7.25' 38 | 39 | // DB Drivers 40 | hsqldb_driver_version = '2.4.0' 41 | postgresql_driver_version = '42.1.4' 42 | } 43 | 44 | 45 | 46 | buildscript { 47 | repositories { 48 | mavenCentral() 49 | } 50 | } 51 | 52 | 53 | 54 | //------------------------------------------------------------------------------------------------------------------------ 55 | // Configure - All Projects 56 | //------------------------------------------------------------------------------------------------------------------------ 57 | 58 | allprojects { 59 | repositories { 60 | mavenCentral() 61 | maven { url 'https://repo.spring.io/snapshot' } 62 | maven { url 'https://repo.spring.io/milestone' } 63 | maven { url 'https://jitpack.io' } 64 | maven { url "http://nexus.gluonhq.com/nexus/content/repositories/releases/" } 65 | 66 | } 67 | } 68 | 69 | //------------------------------------------------------------------------------------------------------------------------ 70 | // Configure - Subprojects 71 | //------------------------------------------------------------------------------------------------------------------------ 72 | 73 | configure(subprojects - project(":03-spring")) { 74 | apply plugin: 'java' 75 | 76 | // Gradle-Configuration for JUnit 5 Platform 77 | test { 78 | useJUnitPlatform() 79 | } 80 | 81 | sourceCompatibility = JavaVersion.VERSION_12 82 | targetCompatibility = JavaVersion.VERSION_12 83 | 84 | dependencies { 85 | testRuntime "org.junit.jupiter:junit-jupiter-engine:$junit5_version" 86 | testRuntime "org.junit.vintage:junit-vintage-engine:$junit5_vintage4_version" 87 | } 88 | } 89 | 90 | 91 | 92 | //------------------------------------------------------------------------------------------------------------------------ 93 | // Tasks - Init project 94 | //------------------------------------------------------------------------------------------------------------------------ 95 | 96 | wrapper { 97 | distributionType = Wrapper.DistributionType.ALL 98 | gradleVersion "5.6.3" 99 | } -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | // Hibernate JPA 3 | compile "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:$hibernate_jpa_version" 4 | } 5 | -------------------------------------------------------------------------------- /common/src/main/java/com/dmitrijdrandarov/entities/DummyFruit.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.entities; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.EnumType; 5 | import javax.persistence.Enumerated; 6 | import javax.persistence.Table; 7 | import java.util.stream.Collectors; 8 | import java.util.stream.Stream; 9 | 10 | /** 11 | * Dummy-Pojo-Object extended. 12 | * 13 | * @author dmitrij-drandarov 14 | * @since 14 Feb 2017 15 | */ 16 | @Entity 17 | @Table(name = "DUMMYFRUIT") 18 | public class DummyFruit extends DummyObject { 19 | 20 | public enum TYPE { 21 | ORANGE, APPLE, BANANA 22 | } 23 | 24 | @Enumerated(EnumType.STRING) 25 | private TYPE type; 26 | 27 | public DummyFruit() { 28 | } 29 | 30 | public DummyFruit(Long id, TYPE type, String name, String description, Double value) { 31 | super(id, name, description, value); 32 | this.type = type; 33 | } 34 | 35 | public TYPE getType() { 36 | return type; 37 | } 38 | 39 | public void setType(TYPE type) { 40 | this.type = type; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return Stream.of(super.toString(), type).map(Object::toString).collect(Collectors.joining(", ")); 46 | } 47 | 48 | @Override 49 | public boolean equals(Object o) { 50 | if (this == o) 51 | return true; 52 | if (o == null || getClass() != o.getClass()) 53 | return false; 54 | 55 | DummyFruit that = (DummyFruit) o; 56 | 57 | return that.getValue().compareTo(getValue()) == 0 58 | && (getId() != null ? getId().equals(that.getId()) : that.getId() == null) 59 | && (getName() != null ? getName().equals(that.getName()) : that.getName() == null) 60 | && (getDescription() != null ? getDescription().equals(that.getDescription()) : that.getDescription() == null) 61 | && type == that.getType(); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | int result; 67 | long temp; 68 | result = getId() != null ? getId().hashCode() : 0; 69 | result = 31 * result + (getName() != null ? getName().hashCode() : 0); 70 | result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0); 71 | temp = Double.doubleToLongBits(getValue()); 72 | result = 31 * result + (int) (temp ^ (temp >>> 32)); 73 | result = 31 * result + (type != null ? type.hashCode() : 0); 74 | return result; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /common/src/main/java/com/dmitrijdrandarov/entities/DummyObject.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.entities; 2 | 3 | import javax.persistence.GeneratedValue; 4 | import javax.persistence.GenerationType; 5 | import javax.persistence.Id; 6 | import javax.persistence.MappedSuperclass; 7 | import java.util.stream.Collectors; 8 | import java.util.stream.Stream; 9 | 10 | /** 11 | * Dummy-Pojo-Object. 12 | * 13 | * @author dmitrij-drandarov 14 | * @since 14 Feb 2017 15 | */ 16 | @MappedSuperclass 17 | public class DummyObject { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | private Long id; 22 | private String name; 23 | private String description; 24 | private Double value; 25 | 26 | public DummyObject() { 27 | } 28 | 29 | public DummyObject(Long id, String name, String description, Double value) { 30 | this.id = id; 31 | this.name = name; 32 | this.description = description; 33 | this.value = value; 34 | } 35 | 36 | public Long getId() { 37 | return id; 38 | } 39 | 40 | public void setId(Long id) { 41 | this.id = id; 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public void setName(String name) { 49 | this.name = name; 50 | } 51 | 52 | public String getDescription() { 53 | return description; 54 | } 55 | 56 | public void setDescription(String description) { 57 | this.description = description; 58 | } 59 | 60 | public Double getValue() { 61 | return value; 62 | } 63 | 64 | public void setValue(Double value) { 65 | this.value = value; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return Stream.of(id, name, description, value).map(Object::toString).collect(Collectors.joining(", ")); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /common/src/main/java/com/dmitrijdrandarov/utils/DummyFruitFactory.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.utils; 2 | 3 | import com.dmitrijdrandarov.entities.DummyFruit; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class DummyFruitFactory { 9 | 10 | //------------------------------------------------------------------------------------------------------------------------ 11 | // Single fruits 12 | //------------------------------------------------------------------------------------------------------------------------ 13 | 14 | public DummyFruit createBabyBanana() { 15 | return new DummyFruit(1L, DummyFruit.TYPE.BANANA, "Baby Banana", "It's yellow!", 20.0); 16 | } 17 | 18 | public DummyFruit createGrannySmithApple() { 19 | return new DummyFruit(2L, DummyFruit.TYPE.APPLE, "Granny Smith Apple", "Delicious!", 10.5); 20 | } 21 | 22 | public DummyFruit createGrapefruit() { 23 | return new DummyFruit(3L, DummyFruit.TYPE.ORANGE, "Grapefruit", "It's totally an orange, baka!", 8.5); 24 | } 25 | 26 | public DummyFruit createRedBanana() { 27 | return new DummyFruit(4L, DummyFruit.TYPE.BANANA, "Red Banana", "I'm not sure!", 60.0); 28 | } 29 | 30 | 31 | //------------------------------------------------------------------------------------------------------------------------ 32 | // Fruit lists 33 | //------------------------------------------------------------------------------------------------------------------------ 34 | 35 | /** 36 | * @return List with {@link #createBabyBanana()}, {@link #createGrannySmithApple()}, {@link #createGrapefruit()} 37 | */ 38 | public List createDummyFruits () { 39 | List dummyFruits = new ArrayList<>(); 40 | dummyFruits.add(createBabyBanana()); 41 | dummyFruits.add(createGrannySmithApple()); 42 | dummyFruits.add(createGrapefruit()); 43 | 44 | return dummyFruits; 45 | } 46 | 47 | /** 48 | * @return List with {@link #createBabyBanana()}, {@link #createGrannySmithApple()}, {@link #createGrapefruit()}, {@link #createRedBanana()} 49 | */ 50 | public List createDummyFruits2() { 51 | List dummyFruits = new ArrayList<>(createDummyFruits()); 52 | dummyFruits.add(createRedBanana()); 53 | 54 | return dummyFruits; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /common/src/main/java/com/dmitrijdrandarov/utils/DummyUtil.java: -------------------------------------------------------------------------------- 1 | package com.dmitrijdrandarov.utils; 2 | 3 | /** 4 | * Dummy-Class to demonstrate calculation of doubles. 5 | * 6 | * @author dmitrij-drandarov 7 | * @since 14 Feb 2017 8 | */ 9 | public class DummyUtil { 10 | 11 | public static double calculateTimesThree(double value) { 12 | return value + value + value; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 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 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /img/00_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/00_architecture.png -------------------------------------------------------------------------------- /img/01_displayname_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/01_displayname_result.png -------------------------------------------------------------------------------- /img/02_nestedTests_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/02_nestedTests_result.png -------------------------------------------------------------------------------- /img/03_testFactory_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/03_testFactory_result.png -------------------------------------------------------------------------------- /img/04_benchmarked_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/04_benchmarked_output.png -------------------------------------------------------------------------------- /img/05_jug_final_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/05_jug_final_small.png -------------------------------------------------------------------------------- /img/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/github.png -------------------------------------------------------------------------------- /img/linkedin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/linkedin.png -------------------------------------------------------------------------------- /img/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/twitter.png -------------------------------------------------------------------------------- /img/xing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandarov-io/JUnit-5-Quick-Start-Guide-and-Framework-Support/46de2e85f1bfbd1e596a7c179f19251e5e771541/img/xing.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'common', 2 | '00-junit-best-practice', 3 | '01-junit-5', 4 | '02-assertj', 5 | '03-spring:03-01-spring-boot-4', 6 | '03-spring:03-02-spring-boot-5', 7 | '03-spring:03-03-spring-4', 8 | '03-spring:03-04-spring-5', 9 | '04-testfx', 10 | '05-mockito' --------------------------------------------------------------------------------