diff --git a/README.md b/README.md
index a7c0c0c..11a55fb 100644
--- a/README.md
+++ b/README.md
@@ -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!
@@ -16,8 +18,6 @@ Based on [facebook's Jest framework](https://facebook.github.io/jest/docs/en/sna
-
-
#### How to install using [Maven](https://mvnrepository.com/artifact/io.github.json-snapshot/json-snapshot/1.0.17)
@@ -33,7 +33,7 @@ Add to your pom.xml dependencies section:
```
-#### Usage
+#### Usage (JUnit)
```java
package com.example;
@@ -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
@@ -199,6 +242,7 @@ Start SnapshotMatcher on child classes only:
```java
package com.example;
+
import org.junit.AfterClass;
import org.junit.BeforeClass;
diff --git a/pom.xml b/pom.xml
index b916558..1e872c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
io.github.json-snapshot
json-snapshot
- 1.0.17
+ 1.3.0
jar
json-snapshot
@@ -81,6 +81,11 @@
junit-jupiter-engine
5.3.2
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.3.2
+
org.mockito
mockito-junit-jupiter
@@ -115,6 +120,22 @@
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.3
+
+
+
+ package
+
+ shade
+
+
+
+
+
com.diffplug.spotless
spotless-maven-plugin
@@ -224,4 +245,4 @@
-
\ No newline at end of file
+
diff --git a/src/main/java/io/github/jsonSnapshot/DefaultConfig.java b/src/main/java/io/github/jsonSnapshot/DefaultConfig.java
index cf71b28..737154d 100644
--- a/src/main/java/io/github/jsonSnapshot/DefaultConfig.java
+++ b/src/main/java/io/github/jsonSnapshot/DefaultConfig.java
@@ -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 {}
diff --git a/src/main/java/io/github/jsonSnapshot/JUnitConfig.java b/src/main/java/io/github/jsonSnapshot/JUnitConfig.java
new file mode 100644
index 0000000..03cbb54
--- /dev/null
+++ b/src/main/java/io/github/jsonSnapshot/JUnitConfig.java
@@ -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);
+ }
+ }
+}
diff --git a/src/main/java/io/github/jsonSnapshot/Snapshot.java b/src/main/java/io/github/jsonSnapshot/Snapshot.java
index 9d73e9a..332add5 100644
--- a/src/main/java/io/github/jsonSnapshot/Snapshot.java
+++ b/src/main/java/io/github/jsonSnapshot/Snapshot.java
@@ -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;
@@ -17,20 +18,28 @@ public class Snapshot {
private Method method;
+ private String scenario;
+
private Function