├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── OSSMETADATA ├── README.md ├── jexter-core ├── pom.xml └── src │ └── main │ └── java │ └── io │ └── thundra │ └── jexter │ └── core │ ├── envvar │ └── EnvironmentVariableHelper.java │ ├── sysprop │ └── ThreadLocalProperties.java │ └── utils │ ├── ExceptionUtils.java │ └── ReflectionUtils.java ├── jexter-junit4-core ├── pom.xml └── src │ ├── main │ └── java │ │ └── io │ │ └── thundra │ │ └── jexter │ │ └── junit4 │ │ └── core │ │ ├── JexterBaseRule.java │ │ ├── envvar │ │ └── EnvironmentVariableSandboxRule.java │ │ ├── sw │ │ └── StopwatchRule.java │ │ └── sysprop │ │ └── SystemPropertySandboxRule.java │ └── test │ └── java │ └── io │ └── thundra │ └── jexter │ └── junit4 │ └── core │ ├── BaseTest.java │ ├── envvar │ └── EnvironmentVariableSandboxRuleTest.java │ ├── sw │ └── StopwatchRuleTest.java │ └── sysprop │ └── SystemPropertySandboxRuleTest.java ├── jexter-junit5-core ├── pom.xml └── src │ ├── main │ └── java │ │ └── io │ │ └── thundra │ │ └── jexter │ │ └── junit5 │ │ └── core │ │ ├── JexterBaseExtension.java │ │ ├── envvar │ │ ├── EnvironmentVariableSandbox.java │ │ └── EnvironmentVariableSandboxExtension.java │ │ ├── sw │ │ ├── Stopwatch.java │ │ └── StopwatchExtension.java │ │ └── sysprop │ │ ├── SystemPropertySandbox.java │ │ └── SystemPropertySandboxExtension.java │ └── test │ └── java │ └── io │ └── thundra │ └── jexter │ └── junit5 │ └── core │ ├── BaseTest.java │ ├── envvar │ └── EnvironmentVariableSandboxExtensionTest.java │ ├── sw │ └── StopwatchExtensionTest.java │ └── sysprop │ └── SystemPropertySandboxExtensionTest.java └── pom.xml /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Maven Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | name: Build Maven Package 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up JDK 1.8 17 | uses: actions/setup-java@v1 18 | with: 19 | java-version: 1.8 20 | - name: Cache Maven packages 21 | uses: actions/cache@v2 22 | with: 23 | path: ~/.m2 24 | key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml') }} 25 | restore-keys: ${{ runner.os }}-m2 26 | - name: Build with Maven 27 | run: mvn clean install -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Maven Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version_scale: 7 | description: "Version scale" 8 | required: true 9 | default: "patch" 10 | jobs: 11 | publish: 12 | name: Release to Maven Central 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out repository 16 | uses: actions/checkout@v2 17 | with: 18 | token: ${{ secrets.THUNDRA_DEVOPS_GITHUB_ACCESS_TOKEN }} 19 | - name: Set up JDK 1.8 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 1.8 23 | - name: Configure CI Git User 24 | run: | 25 | git config --global user.email "action@github.com" 26 | git config --global user.name "GitHub Action" 27 | - name: Configure Maven settings.xml 28 | uses: whelk-io/maven-settings-xml-action@v14 29 | with: 30 | servers: >- 31 | [ 32 | { 33 | "id": "ossrh", 34 | "username": "${{ secrets.THUNDRA_SONATYPE_NEXUS_USERNAME}}", 35 | "password": "${{ secrets.THUNDRA_SONATYPE_NEXUS_PASSWORD}}" 36 | } 37 | ] 38 | - name: Import GPG key 39 | uses: crazy-max/ghaction-import-gpg@v3 40 | with: 41 | gpg-private-key: ${{ secrets.THUNDRA_GPG_PRIVATE_KEY }} 42 | passphrase: ${{ secrets.THUNDRA_GPG_PASSPHRASE}} 43 | - name: Increase Minor Version 44 | if: ${{ github.event.inputs.version_scale == 'minor' }} 45 | run: mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.nextMinorVersion}.0-SNAPSHOT versions:commit 46 | - name: Increase Major Version 47 | if: ${{ github.event.inputs.version_scale == 'major' }} 48 | run: mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.nextMajorVersion}.0.0-SNAPSHOT versions:commit 49 | - name: Build and Release with Maven 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.THUNDRA_DEVOPS_GITHUB_ACCESS_TOKEN }} 52 | run: mvn clean package release:clean release:prepare release:perform release:clean --batch-mode -P release 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | 3 | # Pom files 4 | pom.xml.tag 5 | pom.xml.releaseBackup 6 | pom.xml.versionsBackup 7 | pom.xml.next 8 | dependency-reduced-pom.xml 9 | 10 | # Properties files 11 | release.properties 12 | buildNumber.properties 13 | 14 | # Maven files 15 | .mvn/timing.properties 16 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 17 | .mvn/wrapper/maven-wrapper.jar 18 | 19 | # Log files 20 | *.log 21 | 22 | # Package files 23 | *.jar 24 | *.war 25 | *.ear 26 | *.zip 27 | *.tar.gz 28 | *.rar 29 | 30 | # Virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 31 | hs_err_pid* 32 | 33 | # IDEA files 34 | .idea 35 | *.iml 36 | 37 | # MacOS files 38 | .DS_Store 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Thundra, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /OSSMETADATA: -------------------------------------------------------------------------------- 1 | osslifecycle=active 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jexter 2 | 3 | ![Build Status](https://github.com/thundra-io/jexter/actions/workflows/build.yml/badge.svg) 4 | [![License](https://img.shields.io/github/license/thundra-io/jexter.svg)](http://www.apache.org/licenses/LICENSE-2.0) 5 | ![Lifecycle](https://img.shields.io/osslifecycle/thundra-io/jexter.svg) 6 | 7 | Extensions/Plugins for JVM test frameworks (JUnit 4, JUnit 5, ...) 8 | 9 | ## Get Jexter 10 | 11 | Binaries are available from [Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cio.thundra). 12 | 13 | |Group|Artifact|Latest Stable Version| 14 | |-----------|---------------|---------------------| 15 | |io.thundra|jexter-*|[![Maven Central](https://img.shields.io/maven-central/v/io.thundra/jexter.svg)]()| 16 | 17 | For Maven: 18 | 19 | ```xml 20 | 21 | io.thundra 22 | jexter-... 23 | 24 | test 25 | 26 | ``` 27 | 28 | For Gradle: 29 | 30 | ```groovy 31 | testCompile group: 'io.thundra', name: 'jexter-...', version: /*...*/ 32 | ``` 33 | 34 | Below are the various artifacts published: 35 | 36 | |Artifact|Description| 37 | |-----------|---------------| 38 | |[jexter-core](jexter-core)|Core module| 39 | |[jexter-junit4-core](jexter-junit4-core)|JUnit4 core module| 40 | |[jexter-junit5-core](jexter-junit5-core)|JUnit5 core module| 41 | 42 | ## Versioning 43 | 44 | Artifact versions are in `X.Y.Z` format 45 | - `X`: Major version number. 46 | - Increases only when there are big API and/or architectural changes. 47 | - Breaks backward compatibility. 48 | - `Y`: Minor version number. 49 | - Increases for small API changes and big improvements. 50 | - Breaks backward compatibility. 51 | - `Z`: Patch version number. 52 | - Increases for bug fixes and small improvements. 53 | - Doesn’t break backward compatibility. 54 | 55 | ## Requirements 56 | 57 | * JDK 1.8+ (for build and execution) 58 | * Maven 3.x (for build) 59 | 60 | ## Build 61 | 62 | To build: 63 | 64 | ``` 65 | $ git clone git@github.com:thundra-io/jexter.git 66 | $ cd jexter/ 67 | $ mvn clean install 68 | ``` 69 | 70 | ## How to Use 71 | 72 | ### Utilities/Helpers 73 | 74 | #### **`EnvironmentVariableHelper`** 75 | 76 | ##### Set environment variable 77 | ```java 78 | import io.thundra.jexter.core.envvar.EnvironmentVariableHelper; 79 | 80 | ... 81 | 82 | EnvironmentVariableHelper.set("env-var-name", "env-var-value"); 83 | 84 | ``` 85 | 86 | ##### Remove environment variable 87 | ```java 88 | import io.thundra.jexter.core.envvar.EnvironmentVariableHelper; 89 | 90 | ... 91 | 92 | EnvironmentVariableHelper.remove("env-var-name"); 93 | 94 | ``` 95 | 96 | ### JUnit 4 Rules 97 | 98 | #### **`EnvironmentVariableSandboxRule`** 99 | 100 | Stores environment variables before the test and restores them back to original value after the test. 101 | 102 | - Add `jexter-junit4-core` dependency 103 | - Define `EnvironmentVariableSandboxRule` as test or class level rule 104 | 105 | **Method level:** 106 | ```java 107 | import io.thundra.jexter.junit4.core.envvar.EnvironmentVariableSandboxRule; 108 | 109 | ... 110 | 111 | public class TheTest { 112 | 113 | ... 114 | 115 | @Rule 116 | public EnvironmentVariableSandboxRule rule = new EnvironmentVariableSandboxRule(); 117 | 118 | ... 119 | 120 | } 121 | ``` 122 | **Class level:** 123 | ```java 124 | import io.thundra.jexter.junit4.core.envvar.EnvironmentVariableSandboxRule; 125 | 126 | ... 127 | 128 | public class TheTest { 129 | 130 | ... 131 | 132 | @ClassRule 133 | public static EnvironmentVariableSandboxRule rule = new EnvironmentVariableSandboxRule(); 134 | 135 | ... 136 | 137 | } 138 | ``` 139 | 140 | #### **`SystemPropertySandboxRule`** 141 | 142 | Stores system properties before the test and restores them back to original value after the test. 143 | 144 | - Add `jexter-junit4-core` dependency 145 | - Define `SystemPropertySandboxRule` as test or class level rule 146 | 147 | **Method level:** 148 | ```java 149 | import io.thundra.jexter.junit4.core.sysprop.SystemPropertySandboxRule; 150 | 151 | ... 152 | 153 | public class TheTest { 154 | 155 | ... 156 | 157 | @Rule 158 | public SystemPropertySandboxRule rule = new SystemPropertySandboxRule(); 159 | 160 | ... 161 | 162 | } 163 | ``` 164 | **Class level:** 165 | ```java 166 | import io.thundra.jexter.junit4.core.sysprop.SystemPropertySandboxRule; 167 | 168 | ... 169 | 170 | public class TheTest { 171 | 172 | ... 173 | 174 | @ClassRule 175 | public static SystemPropertySandboxRule rule = new SystemPropertySandboxRule(); 176 | 177 | ... 178 | 179 | } 180 | ``` 181 | 182 | #### **`StopwatchRule`** 183 | 184 | Measures the elapsed time of a test or test suite (class) execution. 185 | 186 | - Add `jexter-junit4-core` dependency 187 | - Define `StopwatchRule` as test or class level rule 188 | 189 | **Method level:** 190 | ```java 191 | import io.thundra.jexter.junit4.core.sw.StopwatchRule; 192 | 193 | ... 194 | 195 | public class TheTest { 196 | 197 | ... 198 | 199 | @Rule 200 | public StopwatchRule rule = new StopwatchRule(); 201 | 202 | ... 203 | 204 | } 205 | ``` 206 | **Class level:** 207 | ```java 208 | import io.thundra.jexter.junit4.core.sw.StopwatchRule; 209 | 210 | ... 211 | 212 | public class TheTest { 213 | 214 | ... 215 | 216 | @ClassRule 217 | public static StopwatchRule rule = new StopwatchRule(); 218 | 219 | ... 220 | 221 | } 222 | ``` 223 | 224 | ### JUnit 5 Extension 225 | 226 | #### **`EnvironmentVariableSandboxExtension`** 227 | 228 | Stores environment variables before the test and restores them back to original value after the test. 229 | 230 | - Add `jexter-junit5-core` dependency 231 | - Annotate test method or class by `@EnvironmentVariableSandbox` annotation 232 | 233 | **Method level:** 234 | ```java 235 | import io.thundra.jexter.junit5.core.envvar.EnvironmentVariableSandbox; 236 | 237 | ... 238 | 239 | public class TheTest { 240 | 241 | ... 242 | 243 | @EnvironmentVariableSandbox 244 | @Test 245 | public void test() { 246 | ... 247 | } 248 | 249 | ... 250 | 251 | } 252 | ``` 253 | **Class level:** 254 | ```java 255 | import io.thundra.jexter.junit5.core.envvar.EnvironmentVariableSandbox; 256 | 257 | ... 258 | 259 | @EnvironmentVariableSandbox 260 | public class TheTest { 261 | 262 | ... 263 | 264 | @Test 265 | public void test() { 266 | ... 267 | } 268 | 269 | ... 270 | 271 | } 272 | ``` 273 | 274 | #### **`SystemPropertySandboxExtension`** 275 | 276 | Stores system properties before the test and restores them back to original value after the test. 277 | 278 | - Add `jexter-junit5-core` dependency 279 | - Annotate test method or class by `@SystemPropertySandbox` annotation 280 | 281 | **Method level:** 282 | ```java 283 | import io.thundra.jexter.junit5.core.sysprop.SystemPropertySandbox; 284 | 285 | ... 286 | 287 | public class TheTest { 288 | 289 | ... 290 | 291 | @SystemPropertySandbox 292 | @Test 293 | public void test() { 294 | ... 295 | } 296 | 297 | ... 298 | 299 | } 300 | ``` 301 | **Class level:** 302 | ```java 303 | import io.thundra.jexter.junit5.core.sysprop.SystemPropertySandbox; 304 | 305 | ... 306 | 307 | @SystemPropertySandbox 308 | public class TheTest { 309 | 310 | ... 311 | 312 | @Test 313 | public void test() { 314 | ... 315 | } 316 | 317 | ... 318 | 319 | } 320 | ``` 321 | 322 | #### **`StopwatchExtension`** 323 | 324 | Measures the elapsed time of a test or test suite (class) execution. 325 | 326 | - Add `jexter-junit5-core` dependency 327 | - Annotate test method or class by `@Stopwatch` annotation 328 | 329 | **Method level:** 330 | ```java 331 | import io.thundra.jexter.junit5.core.sw.Stopwatch; 332 | 333 | ... 334 | 335 | public class TheTest { 336 | 337 | ... 338 | 339 | @Stopwatch 340 | @Test 341 | public void test() { 342 | ... 343 | } 344 | 345 | ... 346 | 347 | } 348 | ``` 349 | **Class level:** 350 | ```java 351 | import io.thundra.jexter.junit5.core.sw.Stopwatch; 352 | 353 | ... 354 | 355 | @Stopwatch 356 | public class TheTest { 357 | 358 | ... 359 | 360 | @Test 361 | public void test() { 362 | ... 363 | } 364 | 365 | ... 366 | 367 | } 368 | ``` 369 | 370 | ## Issues and Feedback 371 | 372 | [![Issues](https://img.shields.io/github/issues/thundra-io/jexter.svg)](https://github.com/thundra-io/jexter/issues?q=is%3Aopen+is%3Aissue) 373 | [![Closed issues](https://img.shields.io/github/issues-closed/thundra-io/jexter.svg)](https://github.com/thundra-io/jexter/issues?q=is%3Aissue+is%3Aclosed) 374 | 375 | Please use [GitHub Issues](https://github.com/thundra-io/jexter/issues) for any bug report, feature request and support. 376 | 377 | ## Contribution 378 | 379 | [![Pull requests](https://img.shields.io/github/issues-pr/thundra-io/jexter.svg)](https://github.com/thundra-io/jexter/pulls?q=is%3Aopen+is%3Apr) 380 | [![Closed pull requests](https://img.shields.io/github/issues-pr-closed/thundra-io/jexter.svg)](https://github.com/thundra-io/jexter/pulls?q=is%3Apr+is%3Aclosed) 381 | [![Contributors](https://img.shields.io/github/contributors/thundra-io/jexter.svg)]() 382 | 383 | If you would like to contribute, please 384 | - Fork the repository on GitHub and clone your fork. 385 | - Create a branch for your changes and make your changes on it. 386 | - Send a pull request by explaining clearly what is your contribution. 387 | 388 | > Tip: Please check the existing pull requests for similar contributions and consider submit an issue to discuss the proposed feature before writing code. 389 | 390 | ## LICENSE 391 | 392 | Copyright (c) 2018 Thundra, Inc. 393 | 394 | Licensed under the Apache License, Version 2.0 (the "License"); 395 | you may not use this file except in compliance with the License. 396 | You may obtain a copy of the License at 397 | 398 | 399 | 400 | Unless required by applicable law or agreed to in writing, software 401 | distributed under the License is distributed on an "AS IS" BASIS, 402 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 403 | See the License for the specific language governing permissions and 404 | limitations under the License. 405 | -------------------------------------------------------------------------------- /jexter-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.thundra 7 | jexter 8 | 1.0.2-SNAPSHOT 9 | 10 | 11 | io.thundra 12 | jexter-core 13 | jexter-core 14 | jar 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /jexter-core/src/main/java/io/thundra/jexter/core/envvar/EnvironmentVariableHelper.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.core.envvar; 2 | 3 | import io.thundra.jexter.core.utils.ReflectionUtils; 4 | 5 | import java.util.Collections; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * Helper class for environment variable related stuff. 12 | * 13 | * @author serkan 14 | */ 15 | public final class EnvironmentVariableHelper { 16 | 17 | private EnvironmentVariableHelper() { 18 | } 19 | 20 | /** 21 | * Gets a value of an environment variable. 22 | * 23 | * @param name name of the environment variable 24 | */ 25 | public static String get(String name) { 26 | return System.getenv(name); 27 | } 28 | 29 | /** 30 | * Gets all environment variables. 31 | * 32 | * @return the all environment variables 33 | */ 34 | public static Map getAll() { 35 | return System.getenv(); 36 | } 37 | 38 | /** 39 | * Gets all environment variables as copy. 40 | * 41 | * @return copy of the all environment variables 42 | */ 43 | public static Map getAllCopy() { 44 | return Collections.unmodifiableMap(new HashMap<>(System.getenv())); 45 | } 46 | 47 | /** 48 | * Set a value of an environment variable. 49 | * 50 | * @param name name of the environment variable 51 | * @param value value of the environment variable 52 | * 53 | * @return value of the existing environment variable, 54 | * null if not exist 55 | */ 56 | public static String set(String name, String value) { 57 | return modifyEnvironmentVariables(map -> map.put(name, value)); 58 | } 59 | 60 | /** 61 | * Sets the all environment variable to the given ones, removes non-existing ones. 62 | * 63 | * @param envVars the given environment variable to be set 64 | */ 65 | public static void setAll(Map envVars) { 66 | modifyEnvironmentVariables(map -> { 67 | map.clear(); 68 | map.putAll(envVars); 69 | return null; 70 | }); 71 | } 72 | 73 | /** 74 | * Clear an environment variable. 75 | * 76 | * @param name name of the environment variable 77 | * 78 | * @return value of the existing environment variable, 79 | * null if not exist 80 | */ 81 | public static String remove(String name) { 82 | return modifyEnvironmentVariables(map -> map.remove(name)); 83 | } 84 | 85 | private static String modifyEnvironmentVariables(Function, String> function) { 86 | try { 87 | return setInProcessEnvironmentClass(function); 88 | } catch (ReflectiveOperationException ex) { 89 | try { 90 | return setInSystemEnvClass(function); 91 | } catch (ReflectiveOperationException ex2) { 92 | ex.addSuppressed(ex2); 93 | throw new UnsupportedOperationException("Could not modify environment variables", ex); 94 | } 95 | } 96 | } 97 | 98 | /* 99 | * Works on Windows 100 | */ 101 | private static String setInProcessEnvironmentClass(Function, String> function) 102 | throws ReflectiveOperationException { 103 | Class processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); 104 | 105 | // The order of operations is critical here: On some operating systems, theEnvironment is present but 106 | // theCaseInsensitiveEnvironment is not present. In such cases, this method must throw a 107 | // ReflectiveOperationException without modifying theEnvironment. Otherwise, the contents of theEnvironment will 108 | // be corrupted. For this reason, both fields are fetched by reflection before either field is modified. 109 | Map theEnvironment = 110 | ReflectionUtils.getClassField(processEnvironmentClass, "theEnvironment"); 111 | Map theCaseInsensitiveEnvironment = 112 | ReflectionUtils.getClassField(processEnvironmentClass, "theCaseInsensitiveEnvironment"); 113 | 114 | String value = function.apply(theEnvironment); 115 | function.apply(theCaseInsensitiveEnvironment); 116 | 117 | return value; 118 | } 119 | 120 | /* 121 | * Works on Linux and OSX 122 | */ 123 | private static String setInSystemEnvClass(Function, String> function) 124 | throws ReflectiveOperationException { 125 | Map env = System.getenv(); 126 | return function.apply(ReflectionUtils.getObjectField(env, "m")); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /jexter-core/src/main/java/io/thundra/jexter/core/sysprop/ThreadLocalProperties.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.core.sysprop; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.io.PrintStream; 7 | import java.io.PrintWriter; 8 | import java.io.Reader; 9 | import java.io.Writer; 10 | import java.util.Collection; 11 | import java.util.Enumeration; 12 | import java.util.Map; 13 | import java.util.Properties; 14 | import java.util.Set; 15 | 16 | /** 17 | * Thread-local {@link Properties} implementation. 18 | * 19 | * @author serkan 20 | */ 21 | public final class ThreadLocalProperties extends Properties { 22 | 23 | private final ThreadLocal localProperties = new InheritableThreadLocal<>(); 24 | private final Properties globalProperties; 25 | 26 | public ThreadLocalProperties(Properties properties) { 27 | this.globalProperties = properties; 28 | } 29 | 30 | private Properties init(Properties properties) { 31 | for (Map.Entry entry : globalProperties.entrySet()) { 32 | properties.put(entry.getKey(), entry.getValue()); 33 | } 34 | return properties; 35 | } 36 | 37 | private Properties getThreadLocal() { 38 | Properties properties = localProperties.get(); 39 | if (properties == null) { 40 | properties = init(new Properties()); 41 | localProperties.set(properties); 42 | } 43 | return properties; 44 | } 45 | 46 | @Override 47 | public String getProperty(String key) { 48 | return getThreadLocal().getProperty(key); 49 | } 50 | 51 | @Override 52 | public Object setProperty(String key, String value) { 53 | return getThreadLocal().setProperty(key, value); 54 | } 55 | 56 | @Override 57 | public Enumeration propertyNames() { 58 | return getThreadLocal().propertyNames(); 59 | } 60 | 61 | @Override 62 | public Set stringPropertyNames() { 63 | return getThreadLocal().stringPropertyNames(); 64 | } 65 | 66 | @Override 67 | public int size() { 68 | return getThreadLocal().size(); 69 | } 70 | 71 | @Override 72 | public boolean isEmpty() { 73 | return getThreadLocal().isEmpty(); 74 | } 75 | 76 | @Override 77 | public Enumeration keys() { 78 | return getThreadLocal().keys(); 79 | } 80 | 81 | @Override 82 | public Enumeration elements() { 83 | return getThreadLocal().elements(); 84 | } 85 | 86 | @Override 87 | public boolean contains(Object value) { 88 | return getThreadLocal().contains(value); 89 | } 90 | 91 | @Override 92 | public boolean containsValue(Object value) { 93 | return getThreadLocal().containsValue(value); 94 | } 95 | 96 | @Override 97 | public boolean containsKey(Object key) { 98 | return getThreadLocal().containsKey(key); 99 | } 100 | 101 | @Override 102 | public Object get(Object key) { 103 | return getThreadLocal().get(key); 104 | } 105 | 106 | @Override 107 | public Object put(Object key, Object value) { 108 | return getThreadLocal().put(key, value); 109 | } 110 | 111 | @Override 112 | public Object remove(Object key) { 113 | return getThreadLocal().remove(key); 114 | } 115 | 116 | @Override 117 | public void putAll(Map t) { 118 | getThreadLocal().putAll(t); 119 | } 120 | 121 | @Override 122 | public void clear() { 123 | getThreadLocal().clear(); 124 | } 125 | 126 | @Override 127 | public Set keySet() { 128 | return getThreadLocal().keySet(); 129 | } 130 | 131 | @Override 132 | public Set> entrySet() { 133 | return getThreadLocal().entrySet(); 134 | } 135 | 136 | @Override 137 | public Collection values() { 138 | return getThreadLocal().values(); 139 | } 140 | 141 | @Override 142 | public void load(Reader reader) throws IOException { 143 | getThreadLocal().load(reader); 144 | } 145 | 146 | @Override 147 | public void load(InputStream inStream) throws IOException { 148 | getThreadLocal().load(inStream); 149 | } 150 | 151 | @Override 152 | public void save(OutputStream out, String comments) { 153 | getThreadLocal().save(out, comments); 154 | } 155 | 156 | @Override 157 | public void store(Writer writer, String comments) throws IOException { 158 | getThreadLocal().store(writer, comments); 159 | } 160 | 161 | @Override 162 | public void store(OutputStream out, String comments) throws IOException { 163 | getThreadLocal().store(out, comments); 164 | } 165 | 166 | @Override 167 | public void loadFromXML(InputStream in) throws IOException { 168 | getThreadLocal().loadFromXML(in); 169 | } 170 | 171 | @Override 172 | public void storeToXML(OutputStream os, String comment) throws IOException { 173 | getThreadLocal().storeToXML(os, comment); 174 | } 175 | 176 | @Override 177 | public void storeToXML(OutputStream os, String comment, String encoding) throws IOException { 178 | getThreadLocal().storeToXML(os, comment, encoding); 179 | } 180 | 181 | @Override 182 | public String getProperty(String key, String defaultValue) { 183 | return getThreadLocal().getProperty(key, defaultValue); 184 | } 185 | 186 | @Override 187 | public void list(PrintStream out) { 188 | getThreadLocal().list(out); 189 | } 190 | 191 | @Override 192 | public void list(PrintWriter out) { 193 | getThreadLocal().list(out); 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /jexter-core/src/main/java/io/thundra/jexter/core/utils/ExceptionUtils.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.core.utils; 2 | 3 | /** 4 | * Utility class for exception related stuffs. 5 | * 6 | * @author serkan 7 | */ 8 | public final class ExceptionUtils { 9 | 10 | private ExceptionUtils() { 11 | } 12 | 13 | /** 14 | * Throws given {@link Throwable error} 15 | * @param t the {@link Throwable} to be thrown 16 | * @param the {@link Throwable} type 17 | * @return the {@link Throwable} 18 | */ 19 | public static T sneakyThrow(Throwable t) { 20 | ExceptionUtils.sneakyThrowInternal(t); 21 | return (T) t; 22 | } 23 | 24 | private static void sneakyThrowInternal(Throwable t) throws T { 25 | throw (T) t; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /jexter-core/src/main/java/io/thundra/jexter/core/utils/ReflectionUtils.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.core.utils; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Modifier; 5 | 6 | /** 7 | * Utility class for providing reflection related stuff. 8 | * 9 | * @author serkan 10 | */ 11 | public final class ReflectionUtils { 12 | 13 | private ReflectionUtils() { 14 | } 15 | 16 | /** 17 | * Gets the object field value. 18 | * 19 | * @param obj the object which owns the field 20 | * @param fieldName name of the field 21 | * @param type of the field 22 | * 23 | * @return value of the field 24 | * 25 | * @throws NoSuchFieldException if the field is not exist 26 | * @throws IllegalAccessException if the field is not accessible 27 | */ 28 | public static T getObjectField(Object obj, String fieldName) 29 | throws NoSuchFieldException, IllegalAccessException { 30 | Class clazz = obj.getClass(); 31 | while (clazz != Object.class) { 32 | Field field = null; 33 | try { 34 | field = clazz.getDeclaredField(fieldName); 35 | } catch (Throwable t) { 36 | } 37 | if (field != null) { 38 | field.setAccessible(true); 39 | return (T) field.get(obj); 40 | } 41 | clazz = clazz.getSuperclass(); 42 | } 43 | throw new NoSuchFieldException("No such field: " + fieldName); 44 | } 45 | 46 | /** 47 | * Gets the class field value. 48 | * 49 | * @param clazz the class which owns the field 50 | * @param fieldName name of the field 51 | * @param type of the field 52 | * 53 | * @return value of the field 54 | * 55 | * @throws NoSuchFieldException if the field is not exist 56 | * @throws IllegalAccessException if the field is not accessible 57 | */ 58 | public static T getClassField(Class clazz, String fieldName) 59 | throws NoSuchFieldException, IllegalAccessException { 60 | while (clazz != Object.class) { 61 | Field field = null; 62 | try { 63 | field = clazz.getDeclaredField(fieldName); 64 | } catch (Throwable t) { 65 | } 66 | if (field != null) { 67 | field.setAccessible(true); 68 | return (T) field.get(null); 69 | } 70 | clazz = clazz.getSuperclass(); 71 | } 72 | throw new NoSuchFieldException("No such field: " + fieldName); 73 | } 74 | 75 | /** 76 | * Sets the object field value. 77 | * 78 | * @param obj the object which owns the field 79 | * @param fieldName name of the field 80 | * @param value value of the field to be set 81 | * 82 | * @throws NoSuchFieldException if the field is not exist 83 | * @throws IllegalAccessException if the field is not accessible 84 | */ 85 | public static void setObjectField(Object obj, String fieldName, Object value) 86 | throws NoSuchFieldException, IllegalAccessException { 87 | Field modifiersField = Field.class.getDeclaredField("modifiers"); 88 | modifiersField.setAccessible(true); 89 | 90 | Class clazz = obj.getClass(); 91 | while (clazz != Object.class) { 92 | Field field = null; 93 | try { 94 | field = clazz.getDeclaredField(fieldName); 95 | } catch (Throwable t) { 96 | } 97 | if (field != null) { 98 | int modifiers = field.getModifiers(); 99 | modifiersField.setInt(field, modifiers & ~Modifier.FINAL); 100 | try { 101 | field.setAccessible(true); 102 | field.set(obj, value); 103 | } finally { 104 | modifiersField.setInt(field, modifiers); 105 | } 106 | break; 107 | } 108 | clazz = clazz.getSuperclass(); 109 | } 110 | } 111 | 112 | /** 113 | * Sets the class field value. 114 | * 115 | * @param clazz the class which owns the field 116 | * @param fieldName name of the field 117 | * @param value value of the field to be set 118 | * 119 | * @throws NoSuchFieldException if the field is not exist 120 | * @throws IllegalAccessException if the field is not accessible 121 | */ 122 | public static void setClassField(Class clazz, String fieldName, Object value) 123 | throws NoSuchFieldException, IllegalAccessException { 124 | Field modifiersField = Field.class.getDeclaredField("modifiers"); 125 | modifiersField.setAccessible(true); 126 | 127 | while (clazz != Object.class) { 128 | Field field = null; 129 | try { 130 | field = clazz.getDeclaredField(fieldName); 131 | } catch (Throwable t) { 132 | } 133 | if (field != null) { 134 | int modifiers = field.getModifiers(); 135 | modifiersField.setInt(field, modifiers & ~Modifier.FINAL); 136 | try { 137 | field.setAccessible(true); 138 | field.set(null, value); 139 | } finally { 140 | modifiersField.setInt(field, modifiers); 141 | } 142 | break; 143 | } 144 | clazz = clazz.getSuperclass(); 145 | } 146 | } 147 | 148 | } 149 | 150 | -------------------------------------------------------------------------------- /jexter-junit4-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.thundra 7 | jexter 8 | 1.0.2-SNAPSHOT 9 | 10 | 11 | io.thundra 12 | jexter-junit4-core 13 | jexter-junit4-core 14 | jar 15 | 16 | 17 | 4.13.2 18 | 19 | 20 | 21 | 22 | io.thundra 23 | jexter-core 24 | 25 | 26 | 27 | junit 28 | junit 29 | ${junit4.version} 30 | provided 31 | true 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /jexter-junit4-core/src/main/java/io/thundra/jexter/junit4/core/JexterBaseRule.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit4.core; 2 | 3 | import org.junit.rules.TestRule; 4 | import org.junit.runner.Description; 5 | import org.junit.runners.model.Statement; 6 | 7 | /** 8 | * Base class for Jexter {@link TestRule}s 9 | * 10 | * @author serkan 11 | */ 12 | public abstract class JexterBaseRule implements TestRule { 13 | 14 | @Override 15 | public Statement apply(Statement base, Description description) { 16 | return new Statement() { 17 | @Override 18 | public void evaluate() throws Throwable { 19 | C context = onBeforeEvaluate(description); 20 | try { 21 | base.evaluate(); 22 | onEvaluateSuccess(description, context); 23 | } catch (Throwable error) { 24 | onEvaluateError(description, context, error); 25 | throw error; 26 | } finally { 27 | onAfterEvaluate(description, context); 28 | } 29 | } 30 | }; 31 | } 32 | 33 | protected C onBeforeEvaluate(Description description) { 34 | return null; 35 | } 36 | 37 | protected void onEvaluateSuccess(Description description, C context) { 38 | } 39 | 40 | protected void onEvaluateError(Description description, C context, Throwable error) { 41 | } 42 | 43 | protected void onAfterEvaluate(Description description, C context) { 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /jexter-junit4-core/src/main/java/io/thundra/jexter/junit4/core/envvar/EnvironmentVariableSandboxRule.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit4.core.envvar; 2 | 3 | import io.thundra.jexter.core.envvar.EnvironmentVariableHelper; 4 | import io.thundra.jexter.junit4.core.JexterBaseRule; 5 | import org.junit.runner.Description; 6 | 7 | import java.util.Map; 8 | 9 | import static io.thundra.jexter.junit4.core.envvar.EnvironmentVariableSandboxRule.EnvironmentVariablesContext; 10 | 11 | /** 12 | * {@link org.junit.rules.TestRule} implementation 13 | * which stores environment variables before the test 14 | * and restores them back to original value after the test. 15 | * 16 | * @author serkan 17 | */ 18 | public class EnvironmentVariableSandboxRule 19 | extends JexterBaseRule { 20 | 21 | protected static class EnvironmentVariablesContext { 22 | 23 | private final Map envVars; 24 | 25 | private EnvironmentVariablesContext(Map envVars) { 26 | this.envVars = envVars; 27 | } 28 | 29 | } 30 | 31 | @Override 32 | protected EnvironmentVariablesContext onBeforeEvaluate(Description description) { 33 | return new EnvironmentVariablesContext(EnvironmentVariableHelper.getAllCopy()); 34 | } 35 | 36 | @Override 37 | protected void onAfterEvaluate(Description description, EnvironmentVariablesContext context) { 38 | if (context != null) { 39 | EnvironmentVariableHelper.setAll(context.envVars); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /jexter-junit4-core/src/main/java/io/thundra/jexter/junit4/core/sw/StopwatchRule.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit4.core.sw; 2 | 3 | import io.thundra.jexter.junit4.core.JexterBaseRule; 4 | import org.junit.runner.Description; 5 | 6 | import static io.thundra.jexter.junit4.core.sw.StopwatchRule.StopwatchContext; 7 | 8 | /** 9 | * {@link org.junit.rules.TestRule} implementation 10 | * which measures the elapsed time of a test execution. 11 | * 12 | * @author serkan 13 | */ 14 | public class StopwatchRule 15 | extends JexterBaseRule { 16 | 17 | protected static class StopwatchContext { 18 | 19 | private final long startTime; 20 | 21 | private StopwatchContext(long startTime) { 22 | this.startTime = startTime; 23 | } 24 | 25 | } 26 | 27 | @Override 28 | protected StopwatchContext onBeforeEvaluate(Description description) { 29 | return new StopwatchContext(System.currentTimeMillis()); 30 | } 31 | 32 | @Override 33 | protected void onAfterEvaluate(Description description, StopwatchContext context) { 34 | if (context != null) { 35 | long elapsedTime = System.currentTimeMillis() - context.startTime; 36 | if (description.isSuite()) { 37 | System.out.printf( 38 | "Execution of test suite '%s' took [%d] ms.\n", 39 | description.getClassName(), 40 | elapsedTime); 41 | } else { 42 | System.out.printf( 43 | "Execution of test '%s#%s' took [%d] ms.\n", 44 | description.getClassName(), 45 | description.getMethodName(), 46 | elapsedTime); 47 | } 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /jexter-junit4-core/src/main/java/io/thundra/jexter/junit4/core/sysprop/SystemPropertySandboxRule.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit4.core.sysprop; 2 | 3 | import io.thundra.jexter.core.sysprop.ThreadLocalProperties; 4 | import io.thundra.jexter.junit4.core.JexterBaseRule; 5 | import org.junit.runner.Description; 6 | 7 | import java.util.Properties; 8 | 9 | import static io.thundra.jexter.junit4.core.sysprop.SystemPropertySandboxRule.SystemPropertiesContext; 10 | 11 | /** 12 | * {@link org.junit.rules.TestRule} implementation 13 | * which stores system properties before the test 14 | * and restores them back to original value after the test. 15 | * 16 | * @author serkan 17 | */ 18 | public class SystemPropertySandboxRule 19 | extends JexterBaseRule { 20 | 21 | protected static class SystemPropertiesContext { 22 | 23 | private final Properties sysProps; 24 | 25 | private SystemPropertiesContext(Properties sysProps) { 26 | this.sysProps = sysProps; 27 | } 28 | 29 | } 30 | 31 | @Override 32 | protected SystemPropertiesContext onBeforeEvaluate(Description description) { 33 | Properties sysProps = System.getProperties(); 34 | System.setProperties(new ThreadLocalProperties(sysProps)); 35 | return new SystemPropertiesContext(sysProps); 36 | } 37 | 38 | @Override 39 | protected void onAfterEvaluate(Description description, SystemPropertiesContext context) { 40 | if (context != null) { 41 | System.setProperties(context.sysProps); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /jexter-junit4-core/src/test/java/io/thundra/jexter/junit4/core/BaseTest.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit4.core; 2 | 3 | import io.thundra.jexter.core.utils.ExceptionUtils; 4 | import org.junit.runner.JUnitCore; 5 | import org.junit.runners.BlockJUnit4ClassRunner; 6 | import org.junit.runners.model.FrameworkMethod; 7 | import org.junit.runners.model.InitializationError; 8 | 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | /** 13 | * @author serkan 14 | */ 15 | public abstract class BaseTest { 16 | 17 | protected static void runTestMethod(Class testClass, String testMethodName) { 18 | try { 19 | JUnit4MethodRunner runner = JUnit4MethodRunner.create(testClass, testMethodName); 20 | JUnitCore jUnitCore = new JUnitCore(); 21 | jUnitCore.run(runner); 22 | } catch (InitializationError e) { 23 | ExceptionUtils.sneakyThrow(e); 24 | } 25 | } 26 | 27 | private static class JUnit4MethodRunner extends BlockJUnit4ClassRunner { 28 | 29 | private static final ThreadLocal threadLocalMethodName = new ThreadLocal<>(); 30 | 31 | private final String methodName; 32 | 33 | private JUnit4MethodRunner(Class klass, String methodName) throws InitializationError { 34 | super(klass); 35 | this.methodName = methodName; 36 | } 37 | 38 | private static JUnit4MethodRunner create(Class klass, String methodName) throws InitializationError { 39 | threadLocalMethodName.set(methodName); 40 | try { 41 | return new JUnit4MethodRunner(klass, methodName); 42 | } finally { 43 | threadLocalMethodName.remove(); 44 | } 45 | } 46 | 47 | @Override 48 | protected List computeTestMethods() { 49 | String mName = methodName != null ? methodName : threadLocalMethodName.get(); 50 | return super.computeTestMethods(). 51 | stream(). 52 | filter(fm -> fm.getName().equals(mName)). 53 | collect(Collectors.toList()); 54 | } 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /jexter-junit4-core/src/test/java/io/thundra/jexter/junit4/core/envvar/EnvironmentVariableSandboxRuleTest.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit4.core.envvar; 2 | 3 | import io.thundra.jexter.core.envvar.EnvironmentVariableHelper; 4 | import io.thundra.jexter.junit4.core.BaseTest; 5 | import org.junit.Assert; 6 | import org.junit.ClassRule; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | 10 | /** 11 | * @author serkan 12 | */ 13 | public class EnvironmentVariableSandboxRuleTest extends BaseTest { 14 | 15 | private static final String TEST_ENV_VAR_NAME = "key"; 16 | private static final String TEST_ENV_VAR_VALUE = "val"; 17 | private static final String TEST_ENV_VAR_VALUE_UPDATED = "val-updated"; 18 | 19 | @Test 20 | public void setEnvVarShouldBeClearedAfterTestMethod_whenTestMethodAnnotated() { 21 | Assert.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 22 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal1.class, "setEnvVar"); 23 | Assert.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 24 | } 25 | 26 | @Test 27 | public void updatedEnvVarShouldBeRevertedBackAfterTestMethod_whenTestMethodAnnotated() { 28 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 29 | try { 30 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 31 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal1.class, "updateEnvVar"); 32 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 33 | } finally { 34 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 35 | } 36 | } 37 | 38 | @Test 39 | public void clearedEnvVarShouldBePutBackAfterTestMethod_whenTestMethodAnnotated() { 40 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 41 | try { 42 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 43 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal1.class, "clearEnvVar"); 44 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 45 | } finally { 46 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 47 | } 48 | } 49 | 50 | @Test 51 | public void setEnvVarShouldBeClearedAfterTestMethod_whenTestClassAnnotated() { 52 | Assert.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 53 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal2.class, "setEnvVar"); 54 | Assert.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 55 | } 56 | 57 | @Test 58 | public void updatedEnvVarShouldBeRevertedBackAfterTestMethod_whenTestClassAnnotated() { 59 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 60 | try { 61 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 62 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal2.class, "updateEnvVar"); 63 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 64 | } finally { 65 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 66 | } 67 | } 68 | 69 | @Test 70 | public void clearedEnvVarShouldBePutBackAfterTestMethod_whenTestClassAnnotated() { 71 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 72 | try { 73 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 74 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal2.class, "clearEnvVar"); 75 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 76 | } finally { 77 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 78 | } 79 | } 80 | 81 | public static class EnvironmentVariableSandboxRuleTestInternal1 { 82 | 83 | @Rule 84 | public EnvironmentVariableSandboxRule rule = new EnvironmentVariableSandboxRule(); 85 | 86 | @Test 87 | public void setEnvVar() { 88 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 89 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 90 | } 91 | 92 | @Test 93 | public void updateEnvVar() { 94 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE_UPDATED); 95 | Assert.assertEquals(TEST_ENV_VAR_VALUE_UPDATED, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 96 | } 97 | 98 | @Test 99 | public void clearEnvVar() { 100 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 101 | Assert.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 102 | } 103 | 104 | } 105 | 106 | public static class EnvironmentVariableSandboxRuleTestInternal2 { 107 | 108 | @ClassRule 109 | public static EnvironmentVariableSandboxRule rule = new EnvironmentVariableSandboxRule(); 110 | 111 | @Test 112 | public void setEnvVar() { 113 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 114 | Assert.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 115 | } 116 | 117 | @Test 118 | public void updateEnvVar() { 119 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE_UPDATED); 120 | Assert.assertEquals(TEST_ENV_VAR_VALUE_UPDATED, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 121 | } 122 | 123 | @Test 124 | public void clearEnvVar() { 125 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 126 | Assert.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 127 | } 128 | 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /jexter-junit4-core/src/test/java/io/thundra/jexter/junit4/core/sw/StopwatchRuleTest.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit4.core.sw; 2 | 3 | import io.thundra.jexter.junit4.core.BaseTest; 4 | import org.junit.Assert; 5 | import org.junit.ClassRule; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.PrintStream; 11 | 12 | /** 13 | * @author serkan 14 | */ 15 | public class StopwatchRuleTest extends BaseTest { 16 | 17 | @Test 18 | public void elapsedTimeShouldBePrinted_whenTestMethodAnnotated() { 19 | PrintStream out = System.out; 20 | try { 21 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 22 | System.setOut(new PrintStream(baos)); 23 | runTestMethod(StopwatchRuleTest.StopwatchRuleTestInternal1.class, "test"); 24 | String outContent = baos.toString(); 25 | Assert.assertNotNull(outContent); 26 | Assert.assertTrue(outContent.contains("Execution of test")); 27 | } finally { 28 | System.setOut(out); 29 | } 30 | } 31 | 32 | @Test 33 | public void elapsedTimeShouldBePrinted_whenTestClassAnnotated() { 34 | PrintStream out = System.out; 35 | try { 36 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 37 | System.setOut(new PrintStream(baos)); 38 | runTestMethod(StopwatchRuleTest.StopwatchRuleTestInternal2.class, "test"); 39 | String outContent = baos.toString(); 40 | Assert.assertNotNull(outContent); 41 | Assert.assertTrue(outContent.contains("Execution of test suite")); 42 | } finally { 43 | System.setOut(out); 44 | } 45 | } 46 | 47 | public static class StopwatchRuleTestInternal1 { 48 | 49 | @Rule 50 | public StopwatchRule rule = new StopwatchRule(); 51 | 52 | @Test 53 | public void test() { 54 | } 55 | 56 | } 57 | 58 | public static class StopwatchRuleTestInternal2 { 59 | 60 | @ClassRule 61 | public static StopwatchRule rule = new StopwatchRule(); 62 | 63 | @Test 64 | public void test() { 65 | } 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /jexter-junit4-core/src/test/java/io/thundra/jexter/junit4/core/sysprop/SystemPropertySandboxRuleTest.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit4.core.sysprop; 2 | 3 | import io.thundra.jexter.junit4.core.BaseTest; 4 | import org.junit.Assert; 5 | import org.junit.ClassRule; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | 9 | /** 10 | * @author serkan 11 | */ 12 | public class SystemPropertySandboxRuleTest extends BaseTest { 13 | 14 | private static final String TEST_SYS_PROP_NAME = "key"; 15 | private static final String TEST_SYS_PROP_VALUE = "val"; 16 | private static final String TEST_SYS_PROP_VALUE_UPDATED = "val-updated"; 17 | 18 | @Test 19 | public void setSysPropShouldBeClearedAfterTestMethod_whenTestMethodAnnotated() { 20 | Assert.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 21 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal1.class, "setSysProp"); 22 | Assert.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 23 | } 24 | 25 | @Test 26 | public void updatedSysPropShouldBeRevertedBackAfterTestMethod_whenTestMethodAnnotated() { 27 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 28 | try { 29 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 30 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal1.class, "updateSysProp"); 31 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 32 | } finally { 33 | System.clearProperty(TEST_SYS_PROP_NAME); 34 | } 35 | } 36 | 37 | @Test 38 | public void clearedSysPropShouldBePutBackAfterTestMethod_whenTestMethodAnnotated() { 39 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 40 | try { 41 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 42 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal1.class, "clearSysProp"); 43 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 44 | } finally { 45 | System.clearProperty(TEST_SYS_PROP_NAME); 46 | } 47 | } 48 | 49 | @Test 50 | public void setSysPropShouldBeClearedAfterTestMethod_whenTestClassAnnotated() { 51 | Assert.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 52 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal2.class, "setSysProp"); 53 | Assert.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 54 | } 55 | 56 | @Test 57 | public void updatedSysPropShouldBeRevertedBackAfterTestMethod_whenTestClassAnnotated() { 58 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 59 | try { 60 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 61 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal2.class, "updateSysProp"); 62 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 63 | } finally { 64 | System.clearProperty(TEST_SYS_PROP_NAME); 65 | } 66 | } 67 | 68 | @Test 69 | public void clearedSysPropShouldBePutBackAfterTestMethod_whenTestClassAnnotated() { 70 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 71 | try { 72 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 73 | runTestMethod(EnvironmentVariableSandboxRuleTestInternal2.class, "clearSysProp"); 74 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 75 | } finally { 76 | System.clearProperty(TEST_SYS_PROP_NAME); 77 | } 78 | } 79 | 80 | public static class EnvironmentVariableSandboxRuleTestInternal1 { 81 | 82 | @Rule 83 | public SystemPropertySandboxRule rule = new SystemPropertySandboxRule(); 84 | 85 | @Test 86 | public void setSysProp() { 87 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 88 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 89 | } 90 | 91 | @Test 92 | public void updateSysProp() { 93 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE_UPDATED); 94 | Assert.assertEquals(TEST_SYS_PROP_VALUE_UPDATED, System.getProperty(TEST_SYS_PROP_NAME)); 95 | } 96 | 97 | @Test 98 | public void clearSysProp() { 99 | System.clearProperty(TEST_SYS_PROP_NAME); 100 | Assert.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 101 | } 102 | 103 | } 104 | 105 | public static class EnvironmentVariableSandboxRuleTestInternal2 { 106 | 107 | @ClassRule 108 | public static SystemPropertySandboxRule rule = new SystemPropertySandboxRule(); 109 | 110 | @Test 111 | public void setSysProp() { 112 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 113 | Assert.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 114 | } 115 | 116 | @Test 117 | public void updateSysProp() { 118 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE_UPDATED); 119 | Assert.assertEquals(TEST_SYS_PROP_VALUE_UPDATED, System.getProperty(TEST_SYS_PROP_NAME)); 120 | } 121 | 122 | @Test 123 | public void clearSysProp() { 124 | System.clearProperty(TEST_SYS_PROP_NAME); 125 | Assert.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 126 | } 127 | 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /jexter-junit5-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | io.thundra 7 | jexter 8 | 1.0.2-SNAPSHOT 9 | 10 | 11 | io.thundra 12 | jexter-junit5-core 13 | jexter-junit5-core 14 | jar 15 | 16 | 17 | 5.7.1 18 | 1.7.1 19 | 20 | 21 | 22 | 23 | io.thundra 24 | jexter-core 25 | 26 | 27 | 28 | org.junit.jupiter 29 | junit-jupiter 30 | ${junit5.jupiter.version} 31 | provided 32 | true 33 | 34 | 35 | org.junit.jupiter 36 | junit-jupiter-engine 37 | ${junit5.jupiter.version} 38 | test 39 | 40 | 41 | org.junit.platform 42 | junit-platform-launcher 43 | ${junit5.platform.version} 44 | test 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/main/java/io/thundra/jexter/junit5/core/JexterBaseExtension.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core; 2 | 3 | import org.junit.jupiter.api.extension.Extension; 4 | import org.junit.jupiter.api.extension.ExtensionContext; 5 | 6 | /** 7 | * Base class for Jexter {@link Extension}s 8 | * 9 | * @author serkan 10 | */ 11 | public abstract class JexterBaseExtension implements Extension { 12 | 13 | protected ExtensionContext.Store getStore(ExtensionContext context) { 14 | return context.getStore(ExtensionContext.Namespace.create(getClass())); 15 | } 16 | 17 | protected String getStoreKey(ExtensionContext context) { 18 | return context.getUniqueId() + "$" + getClass().getName(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/main/java/io/thundra/jexter/junit5/core/envvar/EnvironmentVariableSandbox.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.envvar; 2 | 3 | import org.junit.jupiter.api.extension.ExtendWith; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Inherited; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * {@code @EnvironmentVariableSandbox} is a JUnit Jupiter extension 13 | * to manage environment variables in a sandbox for a test execution. 14 | * 15 | *

So modifications over environment variables during the text execution are rolled-back 16 | * after the test to prevent effecting subsequent tests.

17 | * 18 | *

{@code EnvironmentVariableSandbox} can be used on the method and 19 | * on the class level. If a class is annotated, environment variables will be 20 | * sandboxed for all tests inside that class.

21 | * 22 | *

WARNING: Java considers environment variables to be immutable, so this extension 23 | * uses reflection to change them. This requires that the {@link SecurityManager} 24 | * allows modifications and can potentially break on different operating systems and 25 | * Java versions. Be aware that this is a fragile solution and consider finding a 26 | * better one for your specific situation. If you're running on Java 9 or later, you 27 | * may have to add {@code --add-opens=java.base/java.util=ALL-UNNAMED} to your test 28 | * execution to prevent warnings or even errors.

29 | * 30 | * @author serkan 31 | */ 32 | @Retention(RetentionPolicy.RUNTIME) 33 | @Target({ ElementType.METHOD, ElementType.TYPE }) 34 | @Inherited 35 | @ExtendWith(EnvironmentVariableSandboxExtension.class) 36 | public @interface EnvironmentVariableSandbox { 37 | } 38 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/main/java/io/thundra/jexter/junit5/core/envvar/EnvironmentVariableSandboxExtension.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.envvar; 2 | 3 | import io.thundra.jexter.core.envvar.EnvironmentVariableHelper; 4 | import io.thundra.jexter.junit5.core.JexterBaseExtension; 5 | import org.junit.jupiter.api.extension.AfterAllCallback; 6 | import org.junit.jupiter.api.extension.AfterEachCallback; 7 | import org.junit.jupiter.api.extension.BeforeAllCallback; 8 | import org.junit.jupiter.api.extension.BeforeEachCallback; 9 | import org.junit.jupiter.api.extension.ExtensionContext; 10 | 11 | import java.util.Map; 12 | 13 | /** 14 | * {@link org.junit.jupiter.api.extension.Extension} implementation 15 | * which stores environment variables before the test 16 | * and restores them back to original value after the test. 17 | * 18 | * @author serkan 19 | */ 20 | public class EnvironmentVariableSandboxExtension 21 | extends JexterBaseExtension 22 | implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback, AfterEachCallback { 23 | 24 | private void storeEnvVars(ExtensionContext context) { 25 | ExtensionContext.Store store = getStore(context); 26 | String storeKey = getStoreKey(context); 27 | EnvVarsBackup envVarsBackup = new EnvVarsBackup(EnvironmentVariableHelper.getAllCopy()); 28 | store.put(storeKey, envVarsBackup); 29 | } 30 | 31 | private void restoreEnvVars(ExtensionContext context) { 32 | ExtensionContext.Store store = getStore(context); 33 | String storeKey = getStoreKey(context); 34 | EnvVarsBackup envVarsBackup = (EnvVarsBackup) store.get(storeKey); 35 | if (envVarsBackup != null) { 36 | EnvironmentVariableHelper.setAll(envVarsBackup.envVars); 37 | } 38 | } 39 | 40 | private static class EnvVarsBackup { 41 | 42 | private final Map envVars; 43 | 44 | public EnvVarsBackup(Map envVars) { 45 | this.envVars = envVars; 46 | } 47 | 48 | } 49 | 50 | @Override 51 | public void beforeAll(ExtensionContext context) throws Exception { 52 | storeEnvVars(context); 53 | } 54 | 55 | @Override 56 | public void beforeEach(ExtensionContext context) throws Exception { 57 | storeEnvVars(context); 58 | } 59 | 60 | @Override 61 | public void afterEach(ExtensionContext context) throws Exception { 62 | restoreEnvVars(context); 63 | } 64 | 65 | @Override 66 | public void afterAll(ExtensionContext context) throws Exception { 67 | restoreEnvVars(context); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/main/java/io/thundra/jexter/junit5/core/sw/Stopwatch.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.sw; 2 | 3 | import org.junit.jupiter.api.extension.ExtendWith; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Inherited; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * {@code @Stopwatch} is a JUnit Jupiter extension 13 | * to measure the elapsed time of a test execution. 14 | * 15 | *

{@code Stopwatch} can be used on the method and 16 | * on the class level. If a class is annotated, total execution time will be 17 | * measured for all tests inside that class.

18 | * 19 | * @author serkan 20 | */ 21 | @Retention(RetentionPolicy.RUNTIME) 22 | @Target({ ElementType.METHOD, ElementType.TYPE }) 23 | @Inherited 24 | @ExtendWith(StopwatchExtension.class) 25 | public @interface Stopwatch { 26 | } 27 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/main/java/io/thundra/jexter/junit5/core/sw/StopwatchExtension.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.sw; 2 | 3 | import io.thundra.jexter.junit5.core.JexterBaseExtension; 4 | import org.junit.jupiter.api.extension.AfterAllCallback; 5 | import org.junit.jupiter.api.extension.AfterEachCallback; 6 | import org.junit.jupiter.api.extension.BeforeAllCallback; 7 | import org.junit.jupiter.api.extension.BeforeEachCallback; 8 | import org.junit.jupiter.api.extension.ExtensionContext; 9 | 10 | import java.lang.reflect.Method; 11 | import java.util.Optional; 12 | 13 | /** 14 | * {@link org.junit.jupiter.api.extension.Extension} implementation 15 | * which measures the elapsed time of a test execution. 16 | * 17 | * @author serkan 18 | */ 19 | public class StopwatchExtension 20 | extends JexterBaseExtension 21 | implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback, AfterEachCallback { 22 | 23 | private void storeStopwatch(ExtensionContext context) { 24 | ExtensionContext.Store store = getStore(context); 25 | String storeKey = getStoreKey(context); 26 | StopwatchExtension.StopwatchBackup stopwatchBackup = 27 | new StopwatchExtension.StopwatchBackup(System.currentTimeMillis()); 28 | store.put(storeKey, stopwatchBackup); 29 | } 30 | 31 | private void restoreStopwatch(ExtensionContext context) { 32 | ExtensionContext.Store store = getStore(context); 33 | String storeKey = getStoreKey(context); 34 | StopwatchExtension.StopwatchBackup stopwatchBackup = (StopwatchExtension.StopwatchBackup) store.get(storeKey); 35 | if (stopwatchBackup != null) { 36 | long elapsedTime = System.currentTimeMillis() - stopwatchBackup.startTime; 37 | Optional testMethod = context.getTestMethod(); 38 | String message; 39 | if (!testMethod.isPresent()) { 40 | message = String.format( 41 | "Execution of test suite '%s' took [%d] ms.", 42 | context.getRequiredTestClass().getName(), 43 | elapsedTime); 44 | } else { 45 | message = String.format( 46 | "Execution of test '%s#%s' took [%d] ms.", 47 | context.getRequiredTestClass().getName(), 48 | testMethod.get().getName(), 49 | elapsedTime); 50 | } 51 | System.out.println(message); 52 | context.publishReportEntry(getStoreKey(context), message); 53 | } 54 | } 55 | 56 | private static class StopwatchBackup { 57 | 58 | private final long startTime; 59 | 60 | public StopwatchBackup(long startTime) { 61 | this.startTime = startTime; 62 | } 63 | 64 | } 65 | 66 | @Override 67 | public void beforeAll(ExtensionContext context) throws Exception { 68 | storeStopwatch(context); 69 | } 70 | 71 | @Override 72 | public void beforeEach(ExtensionContext context) throws Exception { 73 | storeStopwatch(context); 74 | } 75 | 76 | @Override 77 | public void afterEach(ExtensionContext context) throws Exception { 78 | restoreStopwatch(context); 79 | } 80 | 81 | @Override 82 | public void afterAll(ExtensionContext context) throws Exception { 83 | restoreStopwatch(context); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/main/java/io/thundra/jexter/junit5/core/sysprop/SystemPropertySandbox.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.sysprop; 2 | 3 | import org.junit.jupiter.api.extension.ExtendWith; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Inherited; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | /** 12 | * {@code @SystemPropertySandbox} is a JUnit Jupiter extension 13 | * to manage system properties in a sandbox for a test execution. 14 | * 15 | *

So modifications over system properties during the text execution are rolled-back 16 | * after the test to prevent effecting subsequent tests.

17 | * 18 | *

{@code SystemPropertySandbox} can be used on the method and 19 | * on the class level. If a class is annotated, system properties will be 20 | * sandboxed for all tests inside that class.

21 | * 22 | * @author serkan 23 | */ 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Target({ ElementType.METHOD, ElementType.TYPE }) 26 | @Inherited 27 | @ExtendWith(SystemPropertySandboxExtension.class) 28 | public @interface SystemPropertySandbox { 29 | } 30 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/main/java/io/thundra/jexter/junit5/core/sysprop/SystemPropertySandboxExtension.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.sysprop; 2 | 3 | import io.thundra.jexter.core.sysprop.ThreadLocalProperties; 4 | import io.thundra.jexter.junit5.core.JexterBaseExtension; 5 | import org.junit.jupiter.api.extension.AfterAllCallback; 6 | import org.junit.jupiter.api.extension.AfterEachCallback; 7 | import org.junit.jupiter.api.extension.BeforeAllCallback; 8 | import org.junit.jupiter.api.extension.BeforeEachCallback; 9 | import org.junit.jupiter.api.extension.ExtensionContext; 10 | 11 | import java.util.Properties; 12 | 13 | /** 14 | * {@link org.junit.jupiter.api.extension.Extension} implementation 15 | * which stores system properties before the test 16 | * and restores them back to original value after the test. 17 | * 18 | * @author serkan 19 | */ 20 | public class SystemPropertySandboxExtension 21 | extends JexterBaseExtension 22 | implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback, AfterEachCallback { 23 | 24 | private void storeSysProps(ExtensionContext context) { 25 | ExtensionContext.Store store = getStore(context); 26 | String storeKey = getStoreKey(context); 27 | Properties sysProps = System.getProperties(); 28 | System.setProperties(new ThreadLocalProperties(sysProps)); 29 | SysPropsBackup sysPropsBackup = new SysPropsBackup(sysProps); 30 | store.put(storeKey, sysPropsBackup); 31 | } 32 | 33 | private void restoreSysProps(ExtensionContext context) { 34 | ExtensionContext.Store store = getStore(context); 35 | String storeKey = getStoreKey(context); 36 | SysPropsBackup sysPropsBackup = (SysPropsBackup) store.get(storeKey); 37 | if (sysPropsBackup != null) { 38 | System.setProperties(sysPropsBackup.sysProps); 39 | } 40 | } 41 | 42 | private static class SysPropsBackup { 43 | 44 | private final Properties sysProps; 45 | 46 | public SysPropsBackup(Properties sysProps) { 47 | this.sysProps = sysProps; 48 | } 49 | 50 | } 51 | 52 | @Override 53 | public void beforeAll(ExtensionContext context) throws Exception { 54 | storeSysProps(context); 55 | } 56 | 57 | @Override 58 | public void beforeEach(ExtensionContext context) throws Exception { 59 | storeSysProps(context); 60 | } 61 | 62 | @Override 63 | public void afterEach(ExtensionContext context) throws Exception { 64 | restoreSysProps(context); 65 | } 66 | 67 | @Override 68 | public void afterAll(ExtensionContext context) throws Exception { 69 | restoreSysProps(context); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/test/java/io/thundra/jexter/junit5/core/BaseTest.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core; 2 | 3 | import io.thundra.jexter.core.utils.ExceptionUtils; 4 | import org.junit.platform.engine.TestExecutionResult; 5 | import org.junit.platform.launcher.Launcher; 6 | import org.junit.platform.launcher.LauncherDiscoveryRequest; 7 | import org.junit.platform.launcher.TestExecutionListener; 8 | import org.junit.platform.launcher.TestIdentifier; 9 | import org.junit.platform.launcher.TestPlan; 10 | import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; 11 | import org.junit.platform.launcher.core.LauncherFactory; 12 | 13 | import java.util.concurrent.atomic.AtomicReference; 14 | 15 | import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod; 16 | 17 | /** 18 | * @author serkan 19 | */ 20 | public abstract class BaseTest { 21 | 22 | private static final Launcher launcher = LauncherFactory.create(); 23 | 24 | protected static void runTestMethod(Class testClass, String testMethodName) { 25 | LauncherDiscoveryRequest launcherDiscoveryRequest = 26 | LauncherDiscoveryRequestBuilder. 27 | request(). 28 | selectors(selectMethod(testClass, testMethodName)). 29 | build(); 30 | TestPlan testPlan = launcher.discover(launcherDiscoveryRequest); 31 | AtomicReference errorRef = new AtomicReference<>(); 32 | launcher.execute(testPlan, new TestExecutionListener[]{ new TestExecutionListener() { 33 | @Override 34 | public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { 35 | if (testExecutionResult.getThrowable().isPresent()) { 36 | errorRef.set(testExecutionResult.getThrowable().get()); 37 | } 38 | } 39 | }}); 40 | if (errorRef.get() != null) { 41 | ExceptionUtils.sneakyThrow(errorRef.get()); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/test/java/io/thundra/jexter/junit5/core/envvar/EnvironmentVariableSandboxExtensionTest.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.envvar; 2 | 3 | import io.thundra.jexter.core.envvar.EnvironmentVariableHelper; 4 | import io.thundra.jexter.junit5.core.BaseTest; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author serkan 10 | */ 11 | public class EnvironmentVariableSandboxExtensionTest extends BaseTest { 12 | 13 | private static final String TEST_ENV_VAR_NAME = "key"; 14 | private static final String TEST_ENV_VAR_VALUE = "val"; 15 | private static final String TEST_ENV_VAR_VALUE_UPDATED = "val-updated"; 16 | 17 | @Test 18 | public void setEnvVarShouldBeClearedAfterTestMethod_whenTestMethodAnnotated() { 19 | Assertions.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 20 | runTestMethod(EnvironmentVariableSandboxExtensionTestInternal1.class, "setEnvVar"); 21 | Assertions.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 22 | } 23 | 24 | @Test 25 | public void updatedEnvVarShouldBeRevertedBackAfterTestMethod_whenTestMethodAnnotated() { 26 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 27 | try { 28 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 29 | runTestMethod(EnvironmentVariableSandboxExtensionTestInternal1.class, "updateEnvVar"); 30 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 31 | } finally { 32 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 33 | } 34 | } 35 | 36 | @Test 37 | public void clearedEnvVarShouldBePutBackAfterTestMethod_whenTestMethodAnnotated() { 38 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 39 | try { 40 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 41 | runTestMethod(EnvironmentVariableSandboxExtensionTestInternal1.class, "clearEnvVar"); 42 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 43 | } finally { 44 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 45 | } 46 | } 47 | 48 | @Test 49 | public void setEnvVarShouldBeClearedAfterTestMethod_whenTestClassAnnotated() { 50 | Assertions.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 51 | runTestMethod(EnvironmentVariableSandboxExtensionTestInternal2.class, "setEnvVar"); 52 | Assertions.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 53 | } 54 | 55 | @Test 56 | public void updatedEnvVarShouldBeRevertedBackAfterTestMethod_whenTestClassAnnotated() { 57 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 58 | try { 59 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 60 | runTestMethod(EnvironmentVariableSandboxExtensionTestInternal2.class, "updateEnvVar"); 61 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 62 | } finally { 63 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 64 | } 65 | } 66 | 67 | @Test 68 | public void clearedEnvVarShouldBePutBackAfterTestMethod_whenTestClassAnnotated() { 69 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 70 | try { 71 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 72 | runTestMethod(EnvironmentVariableSandboxExtensionTestInternal2.class, "clearEnvVar"); 73 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 74 | } finally { 75 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 76 | } 77 | } 78 | 79 | public static class EnvironmentVariableSandboxExtensionTestInternal1 { 80 | 81 | @Test 82 | @EnvironmentVariableSandbox 83 | public void setEnvVar() { 84 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 85 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 86 | } 87 | 88 | @Test 89 | @EnvironmentVariableSandbox 90 | public void updateEnvVar() { 91 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE_UPDATED); 92 | Assertions.assertEquals(TEST_ENV_VAR_VALUE_UPDATED, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 93 | } 94 | 95 | @Test 96 | @EnvironmentVariableSandbox 97 | public void clearEnvVar() { 98 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 99 | Assertions.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 100 | } 101 | 102 | } 103 | 104 | @EnvironmentVariableSandbox 105 | public static class EnvironmentVariableSandboxExtensionTestInternal2 { 106 | 107 | @Test 108 | public void setEnvVar() { 109 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE); 110 | Assertions.assertEquals(TEST_ENV_VAR_VALUE, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 111 | } 112 | 113 | @Test 114 | public void updateEnvVar() { 115 | EnvironmentVariableHelper.set(TEST_ENV_VAR_NAME, TEST_ENV_VAR_VALUE_UPDATED); 116 | Assertions.assertEquals(TEST_ENV_VAR_VALUE_UPDATED, EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 117 | } 118 | 119 | @Test 120 | public void clearEnvVar() { 121 | EnvironmentVariableHelper.remove(TEST_ENV_VAR_NAME); 122 | Assertions.assertNull(EnvironmentVariableHelper.get(TEST_ENV_VAR_NAME)); 123 | } 124 | 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/test/java/io/thundra/jexter/junit5/core/sw/StopwatchExtensionTest.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.sw; 2 | 3 | import io.thundra.jexter.junit5.core.BaseTest; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.PrintStream; 9 | 10 | /** 11 | * @author serkan 12 | */ 13 | public class StopwatchExtensionTest extends BaseTest { 14 | 15 | @Test 16 | public void elapsedTimeShouldBePrinted_whenTestMethodAnnotated() { 17 | PrintStream out = System.out; 18 | try { 19 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 20 | System.setOut(new PrintStream(baos)); 21 | runTestMethod(StopwatchExtensionTest.StopwatchExtensionTestInternal1.class, "test"); 22 | String outContent = baos.toString(); 23 | Assertions.assertNotNull(outContent); 24 | Assertions.assertTrue(outContent.contains("Execution of test")); 25 | } finally { 26 | System.setOut(out); 27 | } 28 | } 29 | 30 | @Test 31 | public void elapsedTimeShouldBePrinted_whenTestClassAnnotated() { 32 | PrintStream out = System.out; 33 | try { 34 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 35 | System.setOut(new PrintStream(baos)); 36 | runTestMethod(StopwatchExtensionTest.StopwatchExtensionTestInternal2.class, "test"); 37 | String outContent = baos.toString(); 38 | Assertions.assertNotNull(outContent); 39 | Assertions.assertTrue(outContent.contains("Execution of test suite")); 40 | } finally { 41 | System.setOut(out); 42 | } 43 | } 44 | 45 | public static class StopwatchExtensionTestInternal1 { 46 | 47 | @Stopwatch 48 | @Test 49 | public void test() { 50 | } 51 | 52 | } 53 | 54 | @Stopwatch 55 | public static class StopwatchExtensionTestInternal2 { 56 | 57 | @Test 58 | public void test() { 59 | } 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /jexter-junit5-core/src/test/java/io/thundra/jexter/junit5/core/sysprop/SystemPropertySandboxExtensionTest.java: -------------------------------------------------------------------------------- 1 | package io.thundra.jexter.junit5.core.sysprop; 2 | 3 | import io.thundra.jexter.junit5.core.BaseTest; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * @author serkan 9 | */ 10 | public class SystemPropertySandboxExtensionTest extends BaseTest { 11 | 12 | private static final String TEST_SYS_PROP_NAME = "key"; 13 | private static final String TEST_SYS_PROP_VALUE = "val"; 14 | private static final String TEST_SYS_PROP_VALUE_UPDATED = "val-updated"; 15 | 16 | @Test 17 | public void setSysPropShouldBeClearedAfterTestMethod_whenTestMethodAnnotated() { 18 | Assertions.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 19 | runTestMethod(SystemPropertySandboxExtensionTestInternal1.class, "setSysProp"); 20 | Assertions.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 21 | } 22 | 23 | @Test 24 | public void updatedSysPropShouldBeRevertedBackAfterTestMethod_whenTestMethodAnnotated() { 25 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 26 | try { 27 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 28 | runTestMethod(SystemPropertySandboxExtensionTestInternal1.class, "updateSysProp"); 29 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 30 | } finally { 31 | System.clearProperty(TEST_SYS_PROP_NAME); 32 | } 33 | } 34 | 35 | @Test 36 | public void clearedSysPropShouldBePutBackAfterTestMethod_whenTestMethodAnnotated() { 37 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 38 | try { 39 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 40 | runTestMethod(SystemPropertySandboxExtensionTestInternal1.class, "clearSysProp"); 41 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 42 | } finally { 43 | System.clearProperty(TEST_SYS_PROP_NAME); 44 | } 45 | } 46 | 47 | @Test 48 | public void setSysPropShouldBeClearedAfterTestMethod_whenTestClassAnnotated() { 49 | Assertions.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 50 | runTestMethod(SystemPropertySandboxExtensionTestInternal2.class, "setSysProp"); 51 | Assertions.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 52 | } 53 | 54 | @Test 55 | public void updatedSysPropShouldBeRevertedBackAfterTestMethod_whenTestClassAnnotated() { 56 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 57 | try { 58 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 59 | runTestMethod(SystemPropertySandboxExtensionTestInternal2.class, "updateSysProp"); 60 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 61 | } finally { 62 | System.clearProperty(TEST_SYS_PROP_NAME); 63 | } 64 | } 65 | 66 | @Test 67 | public void clearedSysPropShouldBePutBackAfterTestMethod_whenTestClassAnnotated() { 68 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 69 | try { 70 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 71 | runTestMethod(SystemPropertySandboxExtensionTestInternal2.class, "clearSysProp"); 72 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 73 | } finally { 74 | System.clearProperty(TEST_SYS_PROP_NAME); 75 | } 76 | } 77 | 78 | public static class SystemPropertySandboxExtensionTestInternal1 { 79 | 80 | @Test 81 | @SystemPropertySandbox 82 | public void setSysProp() { 83 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 84 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 85 | } 86 | 87 | @Test 88 | @SystemPropertySandbox 89 | public void updateSysProp() { 90 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE_UPDATED); 91 | Assertions.assertEquals(TEST_SYS_PROP_VALUE_UPDATED, System.getProperty(TEST_SYS_PROP_NAME)); 92 | } 93 | 94 | @Test 95 | @SystemPropertySandbox 96 | public void clearSysProp() { 97 | System.clearProperty(TEST_SYS_PROP_NAME); 98 | Assertions.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 99 | } 100 | 101 | } 102 | 103 | @SystemPropertySandbox 104 | public static class SystemPropertySandboxExtensionTestInternal2 { 105 | 106 | @Test 107 | public void setSysProp() { 108 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE); 109 | Assertions.assertEquals(TEST_SYS_PROP_VALUE, System.getProperty(TEST_SYS_PROP_NAME)); 110 | } 111 | 112 | @Test 113 | public void updateSysProp() { 114 | System.setProperty(TEST_SYS_PROP_NAME, TEST_SYS_PROP_VALUE_UPDATED); 115 | Assertions.assertEquals(TEST_SYS_PROP_VALUE_UPDATED, System.getProperty(TEST_SYS_PROP_NAME)); 116 | } 117 | 118 | @Test 119 | public void clearSysProp() { 120 | System.clearProperty(TEST_SYS_PROP_NAME); 121 | Assertions.assertNull(System.getProperty(TEST_SYS_PROP_NAME)); 122 | } 123 | 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.thundra 6 | jexter 7 | 1.0.2-SNAPSHOT 8 | pom 9 | 10 | Jexter 11 | Extensions/Plugins for JVM test frameworks 12 | 13 | https://github.com/thundra-io/jexter 14 | 15 | 16 | 17 | The Apache License, Version 2.0 18 | https://www.apache.org/licenses/LICENSE-2.0.txt 19 | 20 | 21 | 22 | 23 | 24 | Serkan Özal 25 | serkan@thundra.io 26 | Thundra 27 | https://thundra.io 28 | 29 | 30 | 31 | 32 | https://github.com/thundra-io/jexter.git 33 | scm:git:https://${GITHUB_TOKEN}@github.com/thundra-io/jexter.git 34 | scm:git:https://${GITHUB_TOKEN}@github.com/thundra-io/jexter.git 35 | HEAD 36 | 37 | 38 | 39 | 1.8 40 | 1.8 41 | UTF-8 42 | 43 | 44 | 45 | jexter-core 46 | jexter-junit4-core 47 | jexter-junit5-core 48 | 49 | 50 | 51 | 52 | 53 | io.thundra 54 | jexter-core 55 | ${project.version} 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-surefire-plugin 65 | 2.22.0 66 | 67 | 68 | org.apache.maven.plugins 69 | maven-failsafe-plugin 70 | 2.22.0 71 | 72 | 73 | maven-jar-plugin 74 | 3.2.0 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-shade-plugin 79 | 3.2.4 80 | 81 | false 82 | 83 | 84 | 85 | 86 | 87 | 88 | package 89 | 90 | shade 91 | 92 | 93 | 94 | 95 | 96 | org.sonatype.plugins 97 | nexus-staging-maven-plugin 98 | 1.6.8 99 | true 100 | 101 | ossrh 102 | https://s01.oss.sonatype.org/ 103 | true 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | release 112 | 113 | 114 | 115 | org.codehaus.mojo 116 | build-helper-maven-plugin 117 | 3.2.0 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-source-plugin 122 | 2.4 123 | 124 | 125 | attach-sources 126 | 127 | jar-no-fork 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-javadoc-plugin 135 | 2.10.4 136 | 137 | 138 | attach-javadocs 139 | 140 | jar 141 | 142 | 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-gpg-plugin 148 | 1.6 149 | 150 | 151 | sign-artifacts 152 | verify 153 | 154 | sign 155 | 156 | 157 | 158 | --pinentry-mode 159 | loopback 160 | 161 | 162 | 163 | 164 | 165 | 166 | org.apache.maven.plugins 167 | maven-release-plugin 168 | 2.5.3 169 | 170 | 171 | org.apache.maven.scm 172 | maven-scm-provider-gitexe 173 | 1.8.1 174 | 175 | 176 | org.apache.maven.plugins 177 | maven-scm-plugin 178 | 1.8.1 179 | 180 | 181 | 182 | v@{project.version} 183 | [ci skip] 184 | true 185 | release 186 | false 187 | 188 | pom.xml 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | ossrh 200 | https://s01.oss.sonatype.org/content/repositories/snapshots 201 | 202 | 203 | ossrh 204 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 205 | 206 | 207 | 208 | 209 | --------------------------------------------------------------------------------