Skip to content

Add Spock support, support parameterized tests via expectScenario() #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
## Snapshot Testing for [JUnit](https://junit.org/) & [Spock](http://spockframework.org/)

#### Purpose of Snapshot Testing
Snapshots help figuring out whether the output of the modules covered by tests is changed, without doing tons of asserts!

Expand All @@ -16,8 +18,6 @@ Based on [facebook's Jest framework](https://facebook.github.io/jest/docs/en/sna
<a href="https://github.com/json-snapshot/json-snapshot.github.io"><img src="https://assets-cdn.github.com/images/modules/logos_page/GitHub-Mark.png" width="80"></a>




#### How to install using [Maven](https://mvnrepository.com/artifact/io.github.json-snapshot/json-snapshot/1.0.17)


Expand All @@ -33,7 +33,7 @@ Add to your pom.xml dependencies section:
```


#### Usage
#### Usage (JUnit)

```java
package com.example;
Expand Down Expand Up @@ -188,6 +188,49 @@ com.example.ExampleTest.shouldExtractArgsFromFakeMethodWithComplexObject=[

Whenever it runs again, the `expect` method argument will be automatically validated with the `.snap` file. That is why you should commit every `.snap` file created.

#### Usage (Spock)

```groovy
package specs

import static io.github.jsonSnapshot.SnapshotMatcher.expectScenario
import io.github.jsonSnapshot.SnapshotMatcher
import io.github.jsonSnapshot.SpockConfig
import spock.lang.Specification

class MySpec extends Specification {

def cleanupSpec() {
SnapshotMatcher.validateSnapshots()
}

def setupSpec() {
SnapshotMatcher.start(new SpockConfig())
}

def 'Convert #scenario to uppercase'() {

when: 'I convert to uppercase'
def result = MyUtility.toUpperCase(value)

then: 'Should convert letters to uppercase'
expectScenario(scenario, uppercase).toMatchSnapshot()

where:
scenario | value
'letter' | 'a'
'number' | '1'
}
}
```

#### Parameterized testing

In order to support parameterized testing you can use the `expectScenario` method that accepts a scenario as the first parameter.

`expectScenario(scenario, object).toMatchSnapshot()`

You need to ensure all your scenarios are unique within a single method. You will normally pass a combination of your parameters to achieve this.

#### Inheritance

Expand All @@ -199,6 +242,7 @@ Start SnapshotMatcher on child classes only:
```java
package com.example;


import org.junit.AfterClass;
import org.junit.BeforeClass;

Expand Down
25 changes: 23 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.github.json-snapshot</groupId>
<artifactId>json-snapshot</artifactId>
<version>1.0.17</version>
<version>1.3.0</version>
<packaging>jar</packaging>

<name>json-snapshot</name>
Expand Down Expand Up @@ -81,6 +81,11 @@
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
Expand Down Expand Up @@ -115,6 +120,22 @@

<build>
<plugins>
<!-- Maven Shade Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
Expand Down Expand Up @@ -224,4 +245,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
7 changes: 2 additions & 5 deletions src/main/java/io/github/jsonSnapshot/DefaultConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
package io.github.jsonSnapshot;

import lombok.Getter;

public class DefaultConfig implements SnapshotConfig {
@Getter private String filePath = "src/test/java/";
}
/** Alias for JUnitConfig for Backward Compatibility */
public class DefaultConfig extends JUnitConfig {}
60 changes: 60 additions & 0 deletions src/main/java/io/github/jsonSnapshot/JUnitConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.github.jsonSnapshot;

import java.lang.reflect.Method;
import java.util.stream.Stream;

import lombok.Getter;

public class JUnitConfig implements SnapshotConfig {
@Getter private String filePath = "src/test/java/";

@Override
public StackTraceElement findStacktraceElement() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
int elementsToSkip = 1; // Start after stackTrace
while (SnapshotMatcher.class
.getName()
.equals(stackTraceElements[elementsToSkip].getClassName())) {
elementsToSkip++;
}

return Stream.of(stackTraceElements)
.skip(elementsToSkip)
.filter(
stackTraceElement ->
hasTestAnnotation(
SnapshotMatcher.getMethod(
getClassForName(stackTraceElement.getClassName()),
stackTraceElement.getMethodName())))
.findFirst()
.orElseThrow(
() ->
new SnapshotMatchException(
"Could not locate a method with one of supported test annotations"));
}

@Override
public boolean shouldUpdateSnapshot() {
String value = System.getProperty(UPDATE_SNAPSHOTS_PARAMETER);
return value != null && value.toUpperCase().startsWith("T");
}

protected boolean hasTestAnnotation(Method method) {
return
// Junit 4
method.isAnnotationPresent(org.junit.Test.class)
|| method.isAnnotationPresent(org.junit.BeforeClass.class)
// JUnit 5
|| method.isAnnotationPresent(org.junit.jupiter.params.ParameterizedTest.class)
|| method.isAnnotationPresent(org.junit.jupiter.api.Test.class)
|| method.isAnnotationPresent(org.junit.jupiter.api.BeforeAll.class);
}

private Class<?> getClassForName(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
}
14 changes: 12 additions & 2 deletions src/main/java/io/github/jsonSnapshot/Snapshot.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Set;
import java.util.function.Function;

import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.diff.DiffUtils;
import org.assertj.core.util.diff.Patch;

Expand All @@ -17,20 +18,28 @@ public class Snapshot {

private Method method;

private String scenario;

private Function<Object, String> jsonFunction;

private Object[] current;

private final SnapshotConfig snapshotConfig;

Snapshot(
SnapshotConfig snapshotConfig,
SnapshotFile snapshotFile,
Class clazz,
Method method,
String scenario,
Function<Object, String> jsonFunction,
Object... current) {
this.snapshotConfig = snapshotConfig;
this.current = current;
this.snapshotFile = snapshotFile;
this.clazz = clazz;
this.method = method;
this.scenario = scenario;
this.jsonFunction = jsonFunction;
}

Expand All @@ -43,7 +52,7 @@ public void toMatchSnapshot() {
String currentObject = takeSnapshot();

// Match Snapshot
if (rawSnapshot != null) {
if (rawSnapshot != null && !snapshotConfig.shouldUpdateSnapshot()) {
if (!rawSnapshot.trim().equals(currentObject.trim())) {
throw generateDiffError(rawSnapshot, currentObject);
}
Expand Down Expand Up @@ -88,6 +97,7 @@ private String takeSnapshot() {
}

public String getSnapshotName() {
return clazz.getName() + "." + method.getName() + "=";
String scenarioFormat = StringUtils.isBlank(scenario) ? "" : "[" + scenario + "]";
return clazz.getName() + "." + method.getName() + scenarioFormat + "=";
}
}
6 changes: 6 additions & 0 deletions src/main/java/io/github/jsonSnapshot/SnapshotConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package io.github.jsonSnapshot;

public interface SnapshotConfig {
String UPDATE_SNAPSHOTS_PARAMETER = "update-snapshots";

String getFilePath();

StackTraceElement findStacktraceElement();

boolean shouldUpdateSnapshot();
}
6 changes: 5 additions & 1 deletion src/main/java/io/github/jsonSnapshot/SnapshotFile.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package io.github.jsonSnapshot;

import java.io.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
Expand Down
Loading