Skip to content

Commit

Permalink
#patch: Enable snapshot-testing in a superclass
Browse files Browse the repository at this point in the history
Tests can now use `Expect` defined in a superclass
  • Loading branch information
NicklasWallgren authored Apr 22, 2023
1 parent ddfd6ee commit e9521ab
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package au.com.origin.snapshots.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Optional;
import java.util.function.Predicate;
import lombok.experimental.UtilityClass;

@UtilityClass
public class ReflectionUtils {

/**
* Find {@link Field} by given predicate.
*
* <p>Invoke the given predicate on all fields in the target class, going up the class hierarchy
* to get all declared fields.
*
* @param clazz the target class to analyze
* @param predicate the predicate
* @return the field or empty optional
*/
public static Optional<Field> findFieldByPredicate(
final Class<?> clazz, final Predicate<Field> predicate) {
Class<?> targetClass = clazz;

do {
final Field[] fields = targetClass.getDeclaredFields();
for (final Field field : fields) {
if (!predicate.test(field)) {
continue;
}
return Optional.of(field);
}
targetClass = targetClass.getSuperclass();
} while (targetClass != null && targetClass != Object.class);

return Optional.empty();
}

public static void makeAccessible(final Field field) {
if ((!Modifier.isPublic(field.getModifiers())
|| !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|| Modifier.isFinal(field.getModifiers()))
&& !field.isAccessible()) {
field.setAccessible(true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,5 @@ public Snapshot apply(Object object, SnapshotSerializerContext snapshotSerialize
snapshotSerializerContext.getHeader().put("custom2", "anything2");
return super.apply(object, snapshotSerializerContext);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig;
import au.com.origin.snapshots.config.SnapshotConfig;
import au.com.origin.snapshots.config.SnapshotConfigInjector;
import au.com.origin.snapshots.utils.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.junit.runner.Description;
Expand All @@ -14,13 +15,13 @@ class SharedSnapshotHelpers implements SnapshotConfigInjector {

public void injectExpectInstanceVariable(
SnapshotVerifier snapshotVerifier, Method testMethod, Object testInstance) {
Arrays.stream(testInstance.getClass().getDeclaredFields())
.filter(it -> it.getType() == Expect.class)
.findFirst()

ReflectionUtils.findFieldByPredicate(
testInstance.getClass(), (field) -> field.getType() == Expect.class)
.ifPresent(
field -> {
(field) -> {
Expect expect = Expect.of(snapshotVerifier, testMethod);
field.setAccessible(true);
ReflectionUtils.makeAccessible(field);
try {
field.set(testInstance, expect);
} catch (IllegalAccessException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package au.com.origin.snapshots;

import au.com.origin.snapshots.junit4.SnapshotRunner;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;

@RunWith(Enclosed.class)
public class BaseClassTest {

static class TestBase {
Expect expect;
}

@RunWith(SnapshotRunner.class)
public static class NestedClass extends TestBase {

@Test
public void helloWorldTest() {
expect.toMatchSnapshot("Hello World");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
au.com.origin.snapshots.BaseClassTest$NestedClass.helloWorldTest=[
Hello World
]
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
import au.com.origin.snapshots.config.SnapshotConfigInjector;
import au.com.origin.snapshots.exceptions.SnapshotMatchException;
import au.com.origin.snapshots.logging.LoggingHelper;
import au.com.origin.snapshots.utils.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.Arrays;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.extension.*;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor;
import org.junit.jupiter.engine.descriptor.ClassTestDescriptor;

Expand Down Expand Up @@ -104,13 +110,12 @@ public Object resolveParameter(
@Override
public void beforeEach(ExtensionContext context) {
if (context.getTestInstance().isPresent() && context.getTestMethod().isPresent()) {
Arrays.stream(context.getTestClass().get().getDeclaredFields())
.filter(it -> it.getType() == Expect.class)
.findFirst()
ReflectionUtils.findFieldByPredicate(
context.getTestClass().get(), (field) -> field.getType() == Expect.class)
.ifPresent(
field -> {
(field) -> {
Expect expect = Expect.of(snapshotVerifier, context.getTestMethod().get());
field.setAccessible(true);
ReflectionUtils.makeAccessible(field);
try {
field.set(context.getTestInstance().get(), expect);
} catch (IllegalAccessException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package au.com.origin.snapshots;

import au.com.origin.snapshots.junit5.SnapshotExtension;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith({SnapshotExtension.class})
public class BaseClassTest {

class TestBase {
Expect expect;
}

@Nested
@ExtendWith(SnapshotExtension.class)
class NestedClass extends TestBase {

@Test
public void helloWorldTest() {
expect.toMatchSnapshot("Hello World");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
au.com.origin.snapshots.BaseClassTest$NestedClass.helloWorldTest=[
Hello World
]
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package au.com.origin.snapshots.spock

import au.com.origin.snapshots.Expect
import au.com.origin.snapshots.utils.ReflectionUtils
import au.com.origin.snapshots.SnapshotVerifier
import au.com.origin.snapshots.logging.LoggingHelper
import lombok.extern.slf4j.Slf4j
import org.slf4j.LoggerFactory
import org.spockframework.runtime.extension.AbstractMethodInterceptor
import org.spockframework.runtime.extension.IMethodInterceptor
import org.spockframework.runtime.extension.IMethodInvocation

import java.lang.reflect.Method
Expand Down Expand Up @@ -40,13 +39,16 @@ class SnapshotMethodInterceptor extends AbstractMethodInterceptor {
}

private void updateInstanceVariable(Object testInstance, Method testMethod) {
testInstance.class.declaredFields
.find { it.getType() == Expect.class }
?.with {
Expect expect = Expect.of(snapshotVerifier, testMethod)
it.setAccessible(true)
it.set(testInstance, expect)
}
ReflectionUtils.findFieldByPredicate(testInstance.class, { field -> field.getType() == Expect.class })
.ifPresent({ field ->
Expect expect = Expect.of(snapshotVerifier, testMethod);
ReflectionUtils.makeAccessible(field);
try {
field.set(testInstance, expect);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package au.com.origin.snapshots

import org.junit.runner.RunWith
import org.spockframework.runtime.Sputnik
import spock.lang.Specification

@RunWith(Sputnik.class)
class SpecificationBase extends Specification {
Expect expect;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package au.com.origin.snapshots

import au.com.origin.snapshots.annotations.SnapshotName
import au.com.origin.snapshots.spock.EnableSnapshots

@EnableSnapshots
class TestBaseSpec extends SpecificationBase {

@SnapshotName("Should use extension")
def "Should use extension"() {
when:
expect.toMatchSnapshot("Hello World")

then:
true
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Should use extension=[
Hello World
]

0 comments on commit e9521ab

Please sign in to comment.