getSlash() {
76 | return restTemplate.getForEntity(baseUrl + "/foo", String.class);
77 | }
78 | }
79 |
80 | @SpringBootApplication
81 | public static class App {
82 | public static void main(String[] args) {
83 | SpringApplication.run(App.class, args);
84 | }
85 | }
86 |
87 | @Nested
88 | @SpringBootTest(classes = {RestApi.class, App.class},
89 | webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
90 | class InnerSpringTest {
91 | @LocalServerPort
92 | private int serverPort;
93 |
94 | @Test
95 | void canReachServiceThatTalksToWiremock() {
96 | given()
97 | .baseUri("http://localhost:" + serverPort)
98 | .when()
99 | .get("/")
100 | .then()
101 | .statusCode(200)
102 | .body(is("from wiremock"));
103 |
104 | }
105 |
106 | @Test
107 | void canSeeLoggingOutputInSystemOutObject() {
108 | assertThat(systemOut.getText())
109 | .contains(":: Spring Boot :: (v2.7.15)");
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/system-stubs-jupiter/src/test/java/uk/org/webcompere/systemstubs/jupiter/examples/SystemExitUseCase.java:
--------------------------------------------------------------------------------
1 | package uk.org.webcompere.systemstubs.jupiter.examples;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.extension.ExtendWith;
5 | import uk.org.webcompere.systemstubs.jupiter.SystemStub;
6 | import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
7 | import uk.org.webcompere.systemstubs.security.AbortExecutionException;
8 | import uk.org.webcompere.systemstubs.security.SystemExit;
9 |
10 | import static org.assertj.core.api.Assertions.assertThat;
11 | import static org.assertj.core.api.Assertions.assertThatThrownBy;
12 |
13 | @ExtendWith(SystemStubsExtension.class)
14 | class SystemExitUseCase {
15 | // the presence of this in the test means System.exit becomes an exception
16 | @SystemStub
17 | private SystemExit systemExit;
18 |
19 | @Test
20 | void doSomethingThatAccidentallyCallsSystemExit() {
21 | // this test would have stopped the JVM, now it ends in `AbortExecutionException`
22 | // System.exit(1);
23 | }
24 |
25 | @Test
26 | void canCatchSystemExit() {
27 | assertThatThrownBy(() -> System.exit(1))
28 | .isInstanceOf(AbortExecutionException.class);
29 |
30 | assertThat(systemExit.getExitCode()).isEqualTo(1);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/system-stubs-jupiter/src/test/java/uk/org/webcompere/systemstubs/jupiter/examples/WithEnvironmentVariables.java:
--------------------------------------------------------------------------------
1 | package uk.org.webcompere.systemstubs.jupiter.examples;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.junit.jupiter.api.extension.ExtendWith;
5 | import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
6 | import uk.org.webcompere.systemstubs.jupiter.SystemStub;
7 | import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
8 |
9 | import static org.assertj.core.api.Assertions.assertThat;
10 |
11 | @ExtendWith(SystemStubsExtension.class)
12 | class WithEnvironmentVariables {
13 |
14 | @SystemStub
15 | private EnvironmentVariables variables = new EnvironmentVariables("input", "foo");
16 |
17 | @Test
18 | void hasAccessToEnvironmentVariables() {
19 | assertThat(System.getenv("input")).isEqualTo("foo");
20 | }
21 |
22 | @Test
23 | void changeEnvironmentVariablesDuringTest() {
24 | variables.set("input", "bar");
25 |
26 | assertThat(System.getenv("input"))
27 | .isEqualTo("bar");
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/system-stubs-testng/README.md:
--------------------------------------------------------------------------------
1 | # System Stubs TestNG
2 |
3 | Provides some automatic instantiation of System Stubs objects during the test lifecycle.
4 |
5 | ```xml
6 |
7 | uk.org.webcompere
8 | system-stubs-testng
9 | 2.1.8
10 |
11 | ```
12 |
13 | ## Options
14 |
15 | ### Stub Without the Plugin
16 | System Stubs Core can be used with TestNG as it is framework agnostic.
17 |
18 | We can call the `setup` method on any of the stubs in a before method, and `teardown` in the after method.
19 |
20 | ```java
21 | private EnvironmentVariables environmentVariables = new EnvironmentVariables();
22 |
23 | @BeforeTest
24 | public void beforeTest() throws Exception {
25 | environmentVariables.set("setinbefore", "yes");
26 |
27 | environmentVariables.setup();
28 | }
29 |
30 | @AfterTest
31 | public void afterTest() throws Exception {
32 | environmentVariables.teardown();
33 | }
34 | ```
35 |
36 | With this code, we'd expect tests to be able to modify the runtime environment by manipulating the
37 | `environmentVariables` object, and we'd expect the tests to see the environment variables set in the `beforeTest`
38 | function; in this example, `setinbefore` was set to `yes`.
39 |
40 | Similarly, we can use `setup` and `teardown` methods of a `TestResources` such as `EnvironmentVariables` inside a test case, or use the `SystemStubs` wrapper methods such as
41 | `withEnvironmentVariables`. See the [main documentation](../README.md) for more on the execute around pattern.
42 |
43 | ### Using the Plugin
44 |
45 | The plugin:
46 |
47 | - Automatically instantiates system stubs objects before they're first used by a TestNG annotated method
48 | - Activates the objects during tests
49 | - Turns the objects off after tests
50 |
51 | Usage:
52 |
53 | ```java
54 | @Listeners(SystemStubsListener.class)
55 | public class CaptureSystemOutTest {
56 |
57 | // We can ininitialise this object with `new` here, or
58 | // leave it uninitialized for the plugin to do it for us
59 | @SystemStub
60 | private SystemOut out;
61 |
62 | @BeforeTest
63 | public void beforeTest() {
64 | out.clear();
65 | }
66 |
67 | @Test
68 | public void canReadThingsSentToSystemOut() {
69 | // simulate the system under test writing to std out
70 | System.out.println("Can I assert this?");
71 |
72 | assertThat(out.getText()).isEqualTo("Can I assert this?\n");
73 | }
74 | }
75 | ```
76 |
77 | > Note: in this instance we've used the `SystemOut` stub. We've had to remember to call its `clear` method as it
78 | > will be shared between tests.
79 | > Note: we may prefer to initialize our stub objects to give them configuration or initial values without using
80 | > a `@BeforeTest` function.
81 |
82 | We can use any of the core system stubs classes such as:
83 |
84 | - `EnvironmentVariables` - for overriding the environment variables
85 | - `SystemProperties` - for temporarily overwriting system properties and then restoring them afterwards
86 | - `SystemOut` - for tapping the `System.out`
87 | - ... and so on
88 |
89 | All we need to do is:
90 |
91 | - Add the `@Listeners(SystemStubsListener.class)` annotation to our TestNG test class (using an array with {} if we have other listeners)
92 | - Add a field for each System Stub we want to use
93 | - Annotate that field with the `@SystemStubs` annotation
94 |
95 | ### Benefits of the Plugin
96 |
97 | With the plugin, there's less boilerplate to write. Any exception handling is also covered by the plugin - or at
98 | least, we don't have to explicitly add `throws` to any of our methods that set up or teardown a stub.
99 |
100 | However, the plugin is simple and opinionated. For fine-grained control of the stubs, the direct method
101 | may sometimes be preferable.
102 |
103 | ## Feedback
104 |
105 | This TestNG module is incubating. Please raise issues with examples if it proves to have issues in practice.
106 |
--------------------------------------------------------------------------------
/system-stubs-testng/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | uk.org.webcompere
7 | system-stubs-parent
8 | 2.1.8
9 | ../pom.xml
10 |
11 |
12 |
13 | system-stubs-testng
14 | 2.1.8
15 | jar
16 |
17 |
18 | 11
19 | 11
20 |
21 |
22 |
23 |
24 | uk.org.webcompere
25 | system-stubs-core
26 |
27 |
28 |
29 | com.github.spotbugs
30 | spotbugs-annotations
31 |
32 |
33 |
34 | org.testng
35 | testng
36 | provided
37 |
38 |
39 |
40 | org.assertj
41 | assertj-core
42 | test
43 |
44 |
45 |
46 |
47 |
48 |
49 | org.apache.maven.plugins
50 | maven-surefire-plugin
51 |
52 |
53 | org.jacoco
54 | jacoco-maven-plugin
55 |
56 |
57 | org.apache.maven.plugins
58 | maven-checkstyle-plugin
59 |
60 |
61 | com.github.spotbugs
62 | spotbugs-maven-plugin
63 |
64 |
65 |
66 |
67 | org.apache.maven.plugins
68 | maven-gpg-plugin
69 |
70 |
71 | org.apache.maven.plugins
72 | maven-source-plugin
73 |
74 |
75 | org.apache.maven.plugins
76 | maven-javadoc-plugin
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/system-stubs-testng/src/main/java/uk/org/webcompere/systemstubs/testng/SystemStub.java:
--------------------------------------------------------------------------------
1 | package uk.org.webcompere.systemstubs.testng;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Marks a field in a test class as a system stub - this causes the {@link SystemStubsListener} to activate
10 | * it during tests. It also causes the field to become instantiated if left uninitialized
11 | * @since 1.0.0
12 | */
13 | @Target({ ElementType.FIELD })
14 | @Retention(RetentionPolicy.RUNTIME)
15 | public @interface SystemStub {
16 | }
17 |
--------------------------------------------------------------------------------
/system-stubs-testng/src/main/java/uk/org/webcompere/systemstubs/testng/SystemStubsListener.java:
--------------------------------------------------------------------------------
1 | package uk.org.webcompere.systemstubs.testng;
2 |
3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4 | import org.testng.IInvokedMethod;
5 | import org.testng.IInvokedMethodListener;
6 | import org.testng.ITestResult;
7 | import uk.org.webcompere.systemstubs.resource.TestResource;
8 |
9 | import java.lang.reflect.AccessibleObject;
10 | import java.lang.reflect.Field;
11 | import java.util.Arrays;
12 | import java.util.List;
13 | import java.util.Objects;
14 |
15 | import static java.util.stream.Collectors.toList;
16 | import static uk.org.webcompere.systemstubs.resource.Resources.executeCleanup;
17 |
18 | /**
19 | * Add this to a test class with:
20 | *
21 | *
22 | * @@Listeners(SystemStubsListener.class)
23 | * public class MyTestClass {
24 | *
25 | * }
26 | *
27 | * This causes any of the system stubs objects, that inherit {@link TestResource} to
28 | * become active during tests. It will also instantiate any objects not initialized in the
29 | * initializer list.
30 | */
31 | public class SystemStubsListener implements IInvokedMethodListener {
32 | @Override
33 | public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
34 | List stubs = ensureAllStubsAreInstantiated(method);
35 | if (method.isTestMethod()) {
36 | try {
37 | for (TestResource stub: stubs) {
38 | stub.setup();
39 | }
40 | } catch (Exception e) {
41 | throw new AssertionError("Could not set up stubs", e);
42 | }
43 | }
44 | }
45 |
46 | @Override
47 | public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
48 | if (method.isTestMethod()) {
49 | try {
50 | executeCleanup(getAllStubs(method));
51 | } catch (Exception e) {
52 | throw new AssertionError("Could not tidy up stubs", e);
53 | }
54 | }
55 | }
56 |
57 | private static TestResource readSystemStubResource(Field field, Object testObject) {
58 | if (!TestResource.class.isAssignableFrom(field.getType())) {
59 | throw new IllegalArgumentException("Cannot use @SystemStub with non TestResource object in field " +
60 | field.getName() + " this one's a " +
61 | field.getType().getCanonicalName());
62 | }
63 | try {
64 | makeAccessible(field);
65 | return (TestResource)field.get(testObject);
66 | } catch (Exception e) {
67 | throw new AssertionError("Cannot read field " + field.getName(), e);
68 | }
69 | }
70 |
71 | private static List getAllStubs(IInvokedMethod method) {
72 | var testObject = method.getTestMethod().getInstance();
73 | var fields = testObject.getClass().getDeclaredFields();
74 | return Arrays.stream(fields)
75 | .filter(field -> field.isAnnotationPresent(SystemStub.class))
76 | .map(field -> readSystemStubResource(field, testObject))
77 | .filter(Objects::nonNull)
78 | .collect(toList());
79 | }
80 |
81 | private static T makeAccessible(T object) {
82 | if (!object.isAccessible()) {
83 | object.setAccessible(true);
84 | }
85 | return object;
86 | }
87 |
88 | private static List ensureAllStubsAreInstantiated(IInvokedMethod method) {
89 | var testObject = method.getTestMethod().getInstance();
90 | var fields = testObject.getClass().getDeclaredFields();
91 | return Arrays.stream(fields)
92 | .filter(field -> field.isAnnotationPresent(SystemStub.class))
93 | .map(field -> instantiateIfNecessary(field, testObject))
94 | .collect(toList());
95 | }
96 |
97 | @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION",
98 | justification = "Generic catch block provided as lots can go wrong when using reflection")
99 | private static TestResource instantiateIfNecessary(Field field, Object testObject) {
100 | if (!TestResource.class.isAssignableFrom(field.getType())) {
101 | throw new IllegalArgumentException("Cannot use @SystemStub with non TestResource object in field " +
102 | field.getName() + " this one's a " +
103 | field.getType().getCanonicalName());
104 | }
105 |
106 | try {
107 | makeAccessible(field);
108 | var currentObject = field.get(testObject);
109 | if (currentObject == null) {
110 | var newInstance = field.getType().getDeclaredConstructor().newInstance();
111 | field.set(testObject, newInstance);
112 | return (TestResource) newInstance;
113 | }
114 | return (TestResource) currentObject;
115 | } catch (Exception e) {
116 | throw new AssertionError("Cannot access field " + field.getName(), e);
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/system-stubs-testng/src/test/java/uk/org/webcompere/systemstubs/testng/SystemStubsPluginWhenStubIsBlankTest.java:
--------------------------------------------------------------------------------
1 | package uk.org.webcompere.systemstubs.testng;
2 |
3 | import org.testng.annotations.BeforeTest;
4 | import org.testng.annotations.Listeners;
5 | import org.testng.annotations.Test;
6 | import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
7 |
8 | import static org.assertj.core.api.Assertions.assertThat;
9 |
10 | @Listeners(SystemStubsListener.class)
11 | public class SystemStubsPluginWhenStubIsBlankTest {
12 |
13 | @SystemStub
14 | private EnvironmentVariables environmentVariables;
15 |
16 | @BeforeTest
17 | public void beforeTest() {
18 | // even though the stub looks to be null, it's instantiated by here
19 | environmentVariables.set("setinbefore", "yes");
20 | }
21 |
22 | @Test
23 | public void noEnvironmentVariable() {
24 | assertThat(System.getenv("scooby")).isBlank();
25 | }
26 |
27 | @Test
28 | public void hasEnvironmentVariable() {
29 | environmentVariables.set("foo", "bar");
30 |
31 | assertThat(System.getenv("foo")).isEqualTo("bar");
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/system-stubs-testng/src/test/java/uk/org/webcompere/systemstubs/testng/SystemStubsPluginWhenStubIsDefinedTest.java:
--------------------------------------------------------------------------------
1 | package uk.org.webcompere.systemstubs.testng;
2 |
3 | import org.testng.IInvokedMethod;
4 | import org.testng.annotations.*;
5 | import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
6 |
7 | import static org.assertj.core.api.Assertions.assertThat;
8 |
9 | @Listeners(SystemStubsListener.class)
10 | public class SystemStubsPluginWhenStubIsDefinedTest {
11 |
12 | @SystemStub
13 | private EnvironmentVariables environmentVariables = new EnvironmentVariables();
14 |
15 | @BeforeTest
16 | public void beforeTest() {
17 | environmentVariables.set("setinbefore", "yes");
18 |
19 | // shouldn't apply yet, as we're not inside a test
20 | assertThat(System.getenv("setinbefore")).isNotEqualTo("yes");
21 | }
22 |
23 | @Test
24 | public void noEnvironmentVariable() {
25 | assertThat(System.getenv("scooby")).isBlank();
26 | }
27 |
28 | @Test
29 | public void hasEnvironmentVariable() {
30 | environmentVariables.set("foo", "bar");
31 |
32 | assertThat(System.getenv("foo")).isEqualTo("bar");
33 | }
34 |
35 | @Test
36 | public void environmentSetInBeforeWillApplyInTest() {
37 | assertThat(System.getenv("setinbefore")).isEqualTo("yes");
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/system-stubs-testng/src/test/java/uk/org/webcompere/systemstubs/testng/examples/CaptureSystemOutTest.java:
--------------------------------------------------------------------------------
1 | package uk.org.webcompere.systemstubs.testng.examples;
2 |
3 | import org.testng.annotations.BeforeTest;
4 | import org.testng.annotations.Listeners;
5 | import org.testng.annotations.Test;
6 | import uk.org.webcompere.systemstubs.stream.SystemOut;
7 | import uk.org.webcompere.systemstubs.testng.SystemStub;
8 | import uk.org.webcompere.systemstubs.testng.SystemStubsListener;
9 |
10 | import static org.assertj.core.api.Assertions.assertThat;
11 |
12 | @Listeners(SystemStubsListener.class)
13 | public class CaptureSystemOutTest {
14 |
15 | @SystemStub
16 | private SystemOut out;
17 |
18 | @BeforeTest
19 | public void beforeTest() {
20 | out.clear();
21 | }
22 |
23 | @Test
24 | public void canReadThingsSentToSystemOut() {
25 | // simulate the system under test writing to std out
26 | System.out.println("Can I assert this?");
27 |
28 | assertThat(out.getText()).startsWith("Can I assert this?");
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/system-stubs-testng/src/test/java/uk/org/webcompere/systemstubs/testng/examples/SystemStubsWithoutPluginTest.java:
--------------------------------------------------------------------------------
1 | package uk.org.webcompere.systemstubs.testng.examples;
2 |
3 | import org.testng.annotations.AfterTest;
4 | import org.testng.annotations.BeforeTest;
5 | import org.testng.annotations.Listeners;
6 | import org.testng.annotations.Test;
7 | import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
8 | import uk.org.webcompere.systemstubs.testng.SystemStub;
9 | import uk.org.webcompere.systemstubs.testng.SystemStubsListener;
10 |
11 | import static org.assertj.core.api.Assertions.assertThat;
12 |
13 | public class SystemStubsWithoutPluginTest {
14 |
15 | private EnvironmentVariables environmentVariables = new EnvironmentVariables();
16 |
17 | @BeforeTest
18 | public void beforeTest() throws Exception {
19 | environmentVariables.set("setinbefore", "yes");
20 |
21 | environmentVariables.setup();
22 | }
23 |
24 | @AfterTest
25 | public void afterTest() throws Exception {
26 | environmentVariables.teardown();
27 | }
28 |
29 | @Test
30 | public void noEnvironmentVariable() {
31 | assertThat(System.getenv("scooby")).isBlank();
32 | }
33 |
34 | @Test
35 | public void hasEnvironmentVariable() {
36 | environmentVariables.set("foo", "bar");
37 |
38 | assertThat(System.getenv("foo")).isEqualTo("bar");
39 | }
40 |
41 | @Test
42 | public void environmentSetInBeforeWillApplyInTest() {
43 | assertThat(System.getenv("setinbefore")).isEqualTo("yes");
44 | }
45 | }
46 |
--------------------------------------------------------------------------------