Skip to content

Commit

Permalink
Merge pull request #66 from webcompere/test-ng
Browse files Browse the repository at this point in the history
Add support for TestNG
  • Loading branch information
ashleyfrieze authored Sep 22, 2023
2 parents fe9775c + ee8b9c9 commit bab4e53
Show file tree
Hide file tree
Showing 10 changed files with 530 additions and 28 deletions.
84 changes: 56 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@
## Overview
System Stubs is used to test code which depends on methods in `java.lang.System`.

It is published under the [MIT license](http://opensource.org/licenses/MIT) and requires at least Java 8. There is a [walkthrough of its main features](https://www.baeldung.com/java-system-stubs) over on [Baeldung.com](https://www.baeldung.com).
The core is test framework agnostic, but there's explicit support for JUnit 4, JUnit 5 and TestNG in
specialist sub-modules.

System Stubs [originated](History.md) as a fork of System Lambda,
originally by Stefan Birkner, and is a partial rewrite and refactor of it. It has diverged in implementation
from the original, but largely [retains compatibility](History.md#execute-around).
It is published under the [MIT license](http://opensource.org/licenses/MIT) and requires at least Java 11.
There is a [walkthrough of its main features](https://www.baeldung.com/java-system-stubs) over on
[Baeldung.com](https://www.baeldung.com).

System Stubs [originated](History.md) as a fork of System Lambda, and is a partial rewrite and refactor of it.
It has diverged in implementation from the original, but largely [retains compatibility](History.md#execute-around).

It is divided into:

Expand All @@ -38,6 +42,35 @@ It is divided into:
- [`system-stubs-junit4`](system-stubs-junit4/README.md) - a set of JUnit4 rules that activate the stubs around test code
- [`system-stubs-jupiter`](system-stubs-jupiter/README.md) - a JUnit 5 extension that automatically injects
System Stubs into JUnit 5 tests.
- [`system-stubs-testng`](system-stubs-testng/README.md) - a plugin/listener for the TestNG framework, which automatically
injects System Stubs into TestNG tests.


## QuickStart (JUnit 5)

```java
@ExtendWith(SystemStubsExtension.class)
class WithEnvironmentVariables {

@SystemStub
private EnvironmentVariables variables =
new EnvironmentVariables("input", "foo");

@Test
void hasAccessToEnvironmentVariables() {
assertThat(System.getenv("input"))
.isEqualTo("foo");
}

@Test
void changeEnvironmentVariablesDuringTest() {
variables.set("input", "bar");

assertThat(System.getenv("input"))
.isEqualTo("bar");
}
}
```

## Installation

Expand Down Expand Up @@ -71,34 +104,28 @@ System Stubs into JUnit 5 tests.
</dependency>
```

## QuickStart (JUnit 5)

```java
@ExtendWith(SystemStubsExtension.class)
class WithEnvironmentVariables {

@SystemStub
private EnvironmentVariables variables =
new EnvironmentVariables("input", "foo");
### TestNG Plugin

@Test
void hasAccessToEnvironmentVariables() {
assertThat(System.getenv("input"))
.isEqualTo("foo");
}

@Test
void changeEnvironmentVariablesDuringTest() {
variables.set("input", "bar");

assertThat(System.getenv("input"))
.isEqualTo("bar");
}
}
```xml
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-testng</artifactId>
<version>2.1.2</version>
</dependency>
```

See the full guide to [JUnit 5](system-stubs-jupiter/README.md), or use it with [JUnit 4](system-stubs-junit4/README.md).

## Catalogue of SystemStubs Objects

- `EnvironmentVariables` - for overriding the environment variables
- `SystemProperties` - for temporarily overwriting system properties and then restoring them afterwards
- `SystemOut` - for tapping the output to `System.out`
- `SystemErr` - for tapping the output to `System.err`
- `SystemErrAndOut` - for tapping the output to both `System.err` and `System.out`
- `SystemIn` - for providing input to `System.in`
- `SystemExit` - prevents system exit from occurring, recording the exit code

## Using System Stubs Individually

You can declare a system stub object:
Expand All @@ -121,6 +148,7 @@ a test, for example:

```java
EnvironmentVariables env = new EnvironmentVariables("HOST", "localhost");

// start controlling the environment
env.setup();

Expand Down Expand Up @@ -187,7 +215,7 @@ the code under the test doesn't use checked exceptions.
This is a good argument for using the JUnit4 or JUnit5 plugins, where you do not
need to specifically turn the stubbing on via the `execute` method.

## Available Stubs
## How to Use Each of the Stubs

### System.exit

Expand Down
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<module>system-stubs-core</module>
<module>system-stubs-junit4</module>
<module>system-stubs-jupiter</module>
<module>system-stubs-testng</module>
</modules>

<properties>
Expand Down Expand Up @@ -119,6 +120,13 @@
<!-- provided scope - the client provides their own JUnit -->
<scope>provided</scope>
</dependency>
<!-- For TestNG Plugin -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
101 changes: 101 additions & 0 deletions system-stubs-testng/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# System Stubs TestNG

Provides some automatic instantiation of System Stubs objects during the test lifecycle.

```xml
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-testng</artifactId>
<version>2.1.1</version>
</dependency>
```

## Options

### Stub Without the Plugin
System Stubs Core can be used with TestNG as it is framework agnostic.

We can call the `setup` method on any of the stubs in a before method, and `teardown` in the after method.

```java
private EnvironmentVariables environmentVariables = new EnvironmentVariables();

@BeforeTest
public void beforeTest() throws Exception {
environmentVariables.set("setinbefore", "yes");

environmentVariables.setup();
}

@AfterTest
public void afterTest() throws Exception {
environmentVariables.teardown();
}
```

With this code, we'd expect tests to be able to modify the runtime environment by manipulating the
`environmentVariables` object, and we'd expect the tests to have an environment variable `setinbefore` set
to `yes`.

Similarly, we can use `setup` and `teardown` inside a test case, or use the `SystemStubs` methods such as
`withEnvironmentVariables`. See the [main documentation](../README.md) for more on the execute around pattern.

### Using of the Plugin

The plugin:

- Automatically instantiates system stubs objects before they're first used by a TestNG annotated method
- Activates the objects during tests
- Turns the objects off after tests

Usage:

```java
@Listeners(SystemStubsListener.class)
public class CaptureSystemOutTest {

@SystemStub
private SystemOut out;

@BeforeTest
public void beforeTest() {
out.clear();
}

@Test
public void canReadThingsSentToSystemOut() {
// simulate the system under test writing to std out
System.out.println("Can I assert this?");

assertThat(out.getText()).isEqualTo("Can I assert this?\n");
}
}
```

> Note: in this instance we've used the `SystemOut` stub. We've had to remember to call its `clear` method as it
> will be shared between tests.
We can use each of the stubs such as:

- `EnvironmentVariables` - for overriding the environment variables
- `SystemProperties` - for temporarily overwriting system properties and then restoring them afterwards
- `SystemOut` - for tapping the `System.out`
- ... and the others

All we need to do is:

- Add the `@Listeners(SystemStubsListener.class)` annotation to our TestNG test class (using an array with {} if we have other listeners)
- Add a field for each System Stub we want to use
- Annotate that field with the `@SystemStubs` annotation

### Benefits of the Plugin

With the plugin, there's less boilerplate to write. Any exception handling is also covered by the plugin - or at
least, we don't have to explicitly add `throws` to any of our methods that set up or teardown a stub.

However, the plugin is simple and opinionated. For fine-grained control of the stubs, the direct method
may sometimes be preferable.

## Feedback

This TestNG module is incubating. Please raise issues with examples if it proves to have issues in practice.
83 changes: 83 additions & 0 deletions system-stubs-testng/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-parent</artifactId>
<version>2.1.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>


<artifactId>system-stubs-testng</artifactId>
<version>2.1.2-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-core</artifactId>
</dependency>

<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
</dependency>

<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
</plugin>

<!-- For release -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package uk.org.webcompere.systemstubs.testng;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Marks a field in a test class as a system stub - this causes the {@link SystemStubsListener} to activate
* it during tests. It also causes the field to become instantiated if left uninitialized
* @since 1.0.0
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface SystemStub {
}
Loading

0 comments on commit bab4e53

Please sign in to comment.