From 895dcc722d0839dd1563ce072d9166fe1cb3a14e Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Tue, 27 Jul 2021 17:55:15 +0100 Subject: [PATCH 01/11] Add new guaranteed field inside a ScenarioState or a ProvidedScenarioState. Fixes #268 Signed-off-by: Andru Stefanescu --- .../annotation/ProvidedScenarioState.java | 2 + .../jgiven/annotation/ScenarioState.java | 2 + ...ssingGuaranteedScenarioStateException.java | 9 ++++ .../jgiven/impl/StageLifecycleManager.java | 33 +++++++++++++++ .../impl/StageLifecycleManagerTest.java | 42 +++++++++++++++++-- 5 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 jgiven-core/src/main/java/com/tngtech/jgiven/exception/JGivenMissingGuaranteedScenarioStateException.java diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/annotation/ProvidedScenarioState.java b/jgiven-core/src/main/java/com/tngtech/jgiven/annotation/ProvidedScenarioState.java index 5e81db69dc..c4b1444818 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/annotation/ProvidedScenarioState.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/annotation/ProvidedScenarioState.java @@ -18,4 +18,6 @@ @Target( ElementType.FIELD ) public @interface ProvidedScenarioState { Resolution resolution() default Resolution.AUTO; + + boolean guaranteed() default false; } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/annotation/ScenarioState.java b/jgiven-core/src/main/java/com/tngtech/jgiven/annotation/ScenarioState.java index ed126b7d4e..be1e450f15 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/annotation/ScenarioState.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/annotation/ScenarioState.java @@ -26,4 +26,6 @@ public enum Resolution { * @since 0.14.0 */ boolean required() default false; + + boolean guaranteed() default false; } diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/exception/JGivenMissingGuaranteedScenarioStateException.java b/jgiven-core/src/main/java/com/tngtech/jgiven/exception/JGivenMissingGuaranteedScenarioStateException.java new file mode 100644 index 0000000000..d22a793bc4 --- /dev/null +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/exception/JGivenMissingGuaranteedScenarioStateException.java @@ -0,0 +1,9 @@ +package com.tngtech.jgiven.exception; + +import java.lang.reflect.Field; + +public class JGivenMissingGuaranteedScenarioStateException extends RuntimeException { + public JGivenMissingGuaranteedScenarioStateException(Field field) { + super("The field " + field.getName() + " is guaranteed but the stage has not initialized it"); + } +} diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java index 6fd930eb80..63ebb39d43 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java @@ -4,10 +4,15 @@ import com.tngtech.jgiven.annotation.AfterStage; import com.tngtech.jgiven.annotation.BeforeScenario; import com.tngtech.jgiven.annotation.BeforeStage; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import com.tngtech.jgiven.annotation.ScenarioState; +import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; import com.tngtech.jgiven.exception.JGivenUserException; import com.tngtech.jgiven.impl.intercept.StepInterceptorImpl; +import com.tngtech.jgiven.impl.util.FieldCache; import com.tngtech.jgiven.impl.util.ReflectionUtil; import java.lang.annotation.Annotation; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; @@ -44,6 +49,7 @@ boolean allAfterStageMethodsHaveBeenExecuted() { void executeAfterStageMethods(boolean fakeExecution) throws Throwable { executeLifecycleMethods(afterStageRegister, fakeExecution); + checkGuaranteedStatesAreInitialized(); //before or after the other after stage methods? } void executeBeforeStageMethods(boolean fakeExecution) throws Throwable { @@ -66,6 +72,33 @@ private void executeLifecycleMethods(LifecycyleMethodManager register, boolea } } + @SuppressWarnings("unchecked") + private void checkGuaranteedStatesAreInitialized() throws JGivenMissingGuaranteedScenarioStateException { + for (Field field: FieldCache.get(instance.getClass()) + .getFieldsWithAnnotation(ProvidedScenarioState.class, ScenarioState.class)) { + if (field.isAnnotationPresent(ProvidedScenarioState.class)) { + if (field.getAnnotation(ProvidedScenarioState.class).guaranteed()) { + checkInialized(field); + } + } + if (field.isAnnotationPresent(ScenarioState.class)) { + if (field.getAnnotation(ScenarioState.class).guaranteed()) { + checkInialized(field); + } + } + } + } + + private void checkInialized(Field field) { + Object value = null; + try { + value = field.get(instance); + } catch (IllegalAccessException e) { } + if (value == null) { + throw new JGivenMissingGuaranteedScenarioStateException(field); + } + } + private enum StepExecutionState { EXECUTED(true), REPEATABLE(false), diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/StageLifecycleManagerTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/StageLifecycleManagerTest.java index 19be59eee0..6bd231e083 100644 --- a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/StageLifecycleManagerTest.java +++ b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/StageLifecycleManagerTest.java @@ -2,10 +2,8 @@ import static org.assertj.core.api.Assertions.assertThat; -import com.tngtech.jgiven.annotation.AfterScenario; -import com.tngtech.jgiven.annotation.AfterStage; -import com.tngtech.jgiven.annotation.BeforeScenario; -import com.tngtech.jgiven.annotation.BeforeStage; +import com.tngtech.jgiven.annotation.*; +import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; import com.tngtech.jgiven.impl.intercept.StepInterceptorImpl; import org.junit.Test; @@ -77,6 +75,42 @@ private void assertAllMethodsHaveBeenExecuted(int times) { assertThat(methodContainer.afterScenarioMethodInvoked).isEqualTo(times); } + @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) + public void null_provided_field_throws_exception() throws Throwable { + FakeStage stageObject = new FakeStage(null, ""); + underTest = new StageLifecycleManager(stageObject, mockInterceptor); + + underTest.executeAfterStageMethods(true); + } + + @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) + public void null_state_field_throws_exception() throws Throwable { + FakeStage stageObject = new FakeStage("", null); + underTest = new StageLifecycleManager(stageObject, mockInterceptor); + + underTest.executeAfterStageMethods(true); + } + + @Test + public void initialized_fields_do_not_interrupt_execution() throws Throwable { + FakeStage stageObject = new FakeStage("", ""); + underTest = new StageLifecycleManager(stageObject, mockInterceptor); + + underTest.executeAfterStageMethods(true); + } + + private class FakeStage { + @ProvidedScenarioState(guaranteed = true) + String providedObject; + @ScenarioState(guaranteed = true) + String stateObject; + + public FakeStage(String providedObject, String stateObject) { + this.providedObject = providedObject; + this.stateObject = stateObject; + } + } + private static class LifecycleMethodContainer { int beforeScenarioMethodInvoked = 0; int afterScenarioMethodInvoked = 0; From cbf1f9b50c677da69996841d464ea720c6df442b Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Wed, 28 Jul 2021 16:24:05 +0100 Subject: [PATCH 02/11] Reformat ValueInjector Signed-off-by: Andru Stefanescu --- .../jgiven/impl/inject/ValueInjector.java | 120 +++++++++--------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java index 36eec3443c..cd7708d21e 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java @@ -1,5 +1,7 @@ package com.tngtech.jgiven.impl.inject; +import static java.util.stream.Collectors.toList; + import com.google.common.collect.Maps; import com.tngtech.jgiven.annotation.ExpectedScenarioState; import com.tngtech.jgiven.annotation.ProvidedScenarioState; @@ -8,132 +10,126 @@ import com.tngtech.jgiven.exception.AmbiguousResolutionException; import com.tngtech.jgiven.exception.JGivenMissingRequiredScenarioStateException; import com.tngtech.jgiven.impl.util.FieldCache; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.lang.reflect.Field; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - -import static java.util.stream.Collectors.toList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Used by Scenario to inject and read values from objects. */ public class ValueInjector { - private static final Logger log = LoggerFactory.getLogger( ValueInjector.class ); - - private final ValueInjectorState state = new ValueInjectorState(); - + private static final Logger log = LoggerFactory.getLogger(ValueInjector.class); /** * Caches all classes that have been already validated for ambiguous resolution. * This avoids duplicate validations of the same class. */ private static final ConcurrentHashMap, Boolean> validatedClasses = new ConcurrentHashMap<>(); + private final ValueInjectorState state = new ValueInjectorState(); /** * @throws AmbiguousResolutionException when multiple fields with the same resolution exist in the given object */ - @SuppressWarnings( "unchecked" ) - public void validateFields( Object object ) { - if( validatedClasses.get( object.getClass() ) == Boolean.TRUE ) { + @SuppressWarnings("unchecked") + public void validateFields(Object object) { + if (validatedClasses.get(object.getClass()) == Boolean.TRUE) { return; } Map resolvedFields = Maps.newHashMap(); - for( ScenarioStateField field : getScenarioFields( object ) ) { - field.getField().setAccessible( true ); + for (ScenarioStateField field : getScenarioFields(object)) { + field.getField().setAccessible(true); Resolution resolution = field.getResolution(); Object key = null; - if( resolution == Resolution.NAME ) { + if (resolution == Resolution.NAME) { key = field.getField().getName(); } else { key = field.getField().getType(); } - if( resolvedFields.containsKey( key ) ) { - Field existingField = resolvedFields.get( key ); - throw new AmbiguousResolutionException( "Ambiguous fields with same " + resolution + " detected. Field 1: " + - existingField + ", field 2: " + field.getField() ); + if (resolvedFields.containsKey(key)) { + Field existingField = resolvedFields.get(key); + throw new AmbiguousResolutionException("Ambiguous fields with same " + resolution + " detected. Field 1: " + + existingField + ", field 2: " + field.getField()); } - resolvedFields.put( key, field.getField() ); + resolvedFields.put(key, field.getField()); } - validatedClasses.put( object.getClass(), Boolean.TRUE ); + validatedClasses.put(object.getClass(), Boolean.TRUE); } - private List getScenarioFields( Object object ) { - @SuppressWarnings( "unchecked" ) + private List getScenarioFields(Object object) { + @SuppressWarnings("unchecked") List scenarioFields = FieldCache - .get( object.getClass() ) - .getFieldsWithAnnotation( ScenarioState.class, ProvidedScenarioState.class, ExpectedScenarioState.class ); + .get(object.getClass()) + .getFieldsWithAnnotation(ScenarioState.class, ProvidedScenarioState.class, ExpectedScenarioState.class); return scenarioFields.stream() - .map( ScenarioStateField.fromField ) - .collect( toList() ); + .map(ScenarioStateField.fromField) + .collect(toList()); } - @SuppressWarnings( "unchecked" ) - public void readValues( Object object ) { - validateFields( object ); - for( ScenarioStateField field : getScenarioFields( object ) ) { + @SuppressWarnings("unchecked") + public void readValues(Object object) { + validateFields(object); + for (ScenarioStateField field : getScenarioFields(object)) { try { - Object value = field.getField().get( object ); - updateValue( field, value ); - log.debug( "Reading value {} from field {}", value, field.getField() ); - } catch( IllegalAccessException e ) { - throw new RuntimeException( "Error while reading field " + field.getField(), e ); + Object value = field.getField().get(object); + updateValue(field, value); + log.debug("Reading value {} from field {}", value, field.getField()); + } catch (IllegalAccessException e) { + throw new RuntimeException("Error while reading field " + field.getField(), e); } } } /** - * * @throws JGivenMissingRequiredScenarioStateException in case a field requires - * a value and the value is not present + * a value and the value is not present */ - @SuppressWarnings( "unchecked" ) - public void updateValues( Object object ) { - validateFields( object ); - for( ScenarioStateField field : getScenarioFields( object ) ) { - Object value = getValue( field ); + @SuppressWarnings("unchecked") + public void updateValues(Object object) { + validateFields(object); + for (ScenarioStateField field : getScenarioFields(object)) { + Object value = getValue(field); - if( value != null ) { + if (value != null) { try { - field.getField().set( object, value ); - } catch( IllegalAccessException e ) { - throw new RuntimeException( "Error while updating field " + field.getField(), e ); + field.getField().set(object, value); + } catch (IllegalAccessException e) { + throw new RuntimeException("Error while updating field " + field.getField(), e); } - log.debug( "Setting field {} to value {}", field.getField(), value ); - } else if( field.isRequired() ) { - throw new JGivenMissingRequiredScenarioStateException( field.getField() ); + log.debug("Setting field {} to value {}", field.getField(), value); + } else if (field.isRequired()) { + throw new JGivenMissingRequiredScenarioStateException(field.getField()); } } } - public void injectValueByType( Class clazz, T value ) { - state.updateValueByType( clazz, value ); + public void injectValueByType(Class clazz, T value) { + state.updateValueByType(clazz, value); } - public void injectValueByName( String name, T value ) { - state.updateValueByName( name, value ); + public void injectValueByName(String name, T value) { + state.updateValueByName(name, value); } - private void updateValue( ScenarioStateField field, Object value ) { - if( field.getResolution() == Resolution.NAME ) { - state.updateValueByName( field.getField().getName(), value ); + private void updateValue(ScenarioStateField field, Object value) { + if (field.getResolution() == Resolution.NAME) { + state.updateValueByName(field.getField().getName(), value); } else { - state.updateValueByType( field.getField().getType(), value ); + state.updateValueByType(field.getField().getType(), value); } } - private Object getValue( ScenarioStateField field ) { - if( field.getResolution() == Resolution.NAME ) { - return state.getValueByName( field.getField().getName() ); + private Object getValue(ScenarioStateField field) { + if (field.getResolution() == Resolution.NAME) { + return state.getValueByName(field.getField().getName()); } - return state.getValueByType( field.getField().getType() ); + return state.getValueByType(field.getField().getType()); } } From 52e448f130ac035a797873eb16b54841e7b0d565 Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Wed, 28 Jul 2021 16:35:17 +0100 Subject: [PATCH 03/11] Change location from LifecycleManager to ValueInjector Signed-off-by: Andru Stefanescu --- .../jgiven/impl/StageLifecycleManager.java | 28 ------------ .../jgiven/impl/inject/ValueInjector.java | 34 +++++++++++++++ .../impl/StageLifecycleManagerTest.java | 42 ++---------------- .../jgiven/impl/inject/ValueInjectorTest.java | 43 +++++++++++++++++++ 4 files changed, 81 insertions(+), 66 deletions(-) create mode 100644 jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java index 63ebb39d43..5bb86a1b6e 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java @@ -49,7 +49,6 @@ boolean allAfterStageMethodsHaveBeenExecuted() { void executeAfterStageMethods(boolean fakeExecution) throws Throwable { executeLifecycleMethods(afterStageRegister, fakeExecution); - checkGuaranteedStatesAreInitialized(); //before or after the other after stage methods? } void executeBeforeStageMethods(boolean fakeExecution) throws Throwable { @@ -72,33 +71,6 @@ private void executeLifecycleMethods(LifecycyleMethodManager register, boolea } } - @SuppressWarnings("unchecked") - private void checkGuaranteedStatesAreInitialized() throws JGivenMissingGuaranteedScenarioStateException { - for (Field field: FieldCache.get(instance.getClass()) - .getFieldsWithAnnotation(ProvidedScenarioState.class, ScenarioState.class)) { - if (field.isAnnotationPresent(ProvidedScenarioState.class)) { - if (field.getAnnotation(ProvidedScenarioState.class).guaranteed()) { - checkInialized(field); - } - } - if (field.isAnnotationPresent(ScenarioState.class)) { - if (field.getAnnotation(ScenarioState.class).guaranteed()) { - checkInialized(field); - } - } - } - } - - private void checkInialized(Field field) { - Object value = null; - try { - value = field.get(instance); - } catch (IllegalAccessException e) { } - if (value == null) { - throw new JGivenMissingGuaranteedScenarioStateException(field); - } - } - private enum StepExecutionState { EXECUTED(true), REPEATABLE(false), diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java index cd7708d21e..37f148634d 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java @@ -8,6 +8,7 @@ import com.tngtech.jgiven.annotation.ScenarioState; import com.tngtech.jgiven.annotation.ScenarioState.Resolution; import com.tngtech.jgiven.exception.AmbiguousResolutionException; +import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; import com.tngtech.jgiven.exception.JGivenMissingRequiredScenarioStateException; import com.tngtech.jgiven.impl.util.FieldCache; import java.lang.reflect.Field; @@ -71,9 +72,15 @@ private List getScenarioFields(Object object) { .collect(toList()); } + /** + * @throws JGivenMissingGuaranteedScenarioStateException in case a field is guaranteed + * and is not initialized by the finishing stage + */ @SuppressWarnings("unchecked") public void readValues(Object object) { validateFields(object); + checkGuaranteedStatesAreInitialized(object); + for (ScenarioStateField field : getScenarioFields(object)) { try { Object value = field.getField().get(object); @@ -132,4 +139,31 @@ private Object getValue(ScenarioStateField field) { return state.getValueByType(field.getField().getType()); } + + private void checkGuaranteedStatesAreInitialized(Object instance) + throws JGivenMissingGuaranteedScenarioStateException { + for (Field field: FieldCache.get(instance.getClass()) + .getFieldsWithAnnotation(ProvidedScenarioState.class, ScenarioState.class)) { + if (field.isAnnotationPresent(ProvidedScenarioState.class)) { + if (field.getAnnotation(ProvidedScenarioState.class).guaranteed()) { + checkInitialized(instance, field); + } + } + if (field.isAnnotationPresent(ScenarioState.class)) { + if (field.getAnnotation(ScenarioState.class).guaranteed()) { + checkInitialized(instance, field); + } + } + } + } + + private void checkInitialized(Object instance, Field field) { + Object value = null; + try { + value = field.get(instance); + } catch (IllegalAccessException e) { } + if (value == null) { + throw new JGivenMissingGuaranteedScenarioStateException(field); + } + } } diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/StageLifecycleManagerTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/StageLifecycleManagerTest.java index 6bd231e083..19be59eee0 100644 --- a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/StageLifecycleManagerTest.java +++ b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/StageLifecycleManagerTest.java @@ -2,8 +2,10 @@ import static org.assertj.core.api.Assertions.assertThat; -import com.tngtech.jgiven.annotation.*; -import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; +import com.tngtech.jgiven.annotation.AfterScenario; +import com.tngtech.jgiven.annotation.AfterStage; +import com.tngtech.jgiven.annotation.BeforeScenario; +import com.tngtech.jgiven.annotation.BeforeStage; import com.tngtech.jgiven.impl.intercept.StepInterceptorImpl; import org.junit.Test; @@ -75,42 +77,6 @@ private void assertAllMethodsHaveBeenExecuted(int times) { assertThat(methodContainer.afterScenarioMethodInvoked).isEqualTo(times); } - @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) - public void null_provided_field_throws_exception() throws Throwable { - FakeStage stageObject = new FakeStage(null, ""); - underTest = new StageLifecycleManager(stageObject, mockInterceptor); - - underTest.executeAfterStageMethods(true); - } - - @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) - public void null_state_field_throws_exception() throws Throwable { - FakeStage stageObject = new FakeStage("", null); - underTest = new StageLifecycleManager(stageObject, mockInterceptor); - - underTest.executeAfterStageMethods(true); - } - - @Test - public void initialized_fields_do_not_interrupt_execution() throws Throwable { - FakeStage stageObject = new FakeStage("", ""); - underTest = new StageLifecycleManager(stageObject, mockInterceptor); - - underTest.executeAfterStageMethods(true); - } - - private class FakeStage { - @ProvidedScenarioState(guaranteed = true) - String providedObject; - @ScenarioState(guaranteed = true) - String stateObject; - - public FakeStage(String providedObject, String stateObject) { - this.providedObject = providedObject; - this.stateObject = stateObject; - } - } - private static class LifecycleMethodContainer { int beforeScenarioMethodInvoked = 0; int afterScenarioMethodInvoked = 0; diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java new file mode 100644 index 0000000000..730b92962d --- /dev/null +++ b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java @@ -0,0 +1,43 @@ +package com.tngtech.jgiven.impl.inject; + +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import com.tngtech.jgiven.annotation.ScenarioState; +import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; +import org.junit.Test; + +public class ValueInjectorTest { + private ValueInjector injector = new ValueInjector(); + + @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) + public void null_provided_field_throws_exception() { + FakeStage stageObject = new FakeStage(null, ""); + + injector.readValues(stageObject); + } + + @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) + public void null_state_field_throws_exception() throws Throwable { + FakeStage stageObject = new FakeStage("", null); + + injector.readValues(stageObject); + } + + @Test + public void initialized_fields_do_not_interrupt_execution() throws Throwable { + FakeStage stageObject = new FakeStage("", ""); + + injector.readValues(stageObject); + } + + private class FakeStage { + @ProvidedScenarioState(guaranteed = true) + String providedObject; + @ScenarioState(guaranteed = true) + String stateObject; + + public FakeStage(String providedObject, String stateObject) { + this.providedObject = providedObject; + this.stateObject = stateObject; + } + } +} From b7a62fd963ba540efaa6041bbd952beb17279d40 Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Wed, 28 Jul 2021 17:19:22 +0100 Subject: [PATCH 04/11] Add JGiven test to ensure order of guaranteed field verification Signed-off-by: Andru Stefanescu --- .../jgiven/impl/GuaranteedStateTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java new file mode 100644 index 0000000000..385cf5d22a --- /dev/null +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java @@ -0,0 +1,51 @@ +package com.tngtech.jgiven.impl; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.BeforeStage; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; +import com.tngtech.jgiven.junit.ScenarioTest; +import org.junit.Test; + + +public class GuaranteedStateTest extends ScenarioTest { + + @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) + public void assure_before_method_of_second_test_is_executed_after_guaranteed_fields_validation() { + given().a_given_stage_with_guaranteed_field_uninitialized(); + then().the_before_is_not_called(); + } + + @Test(expected = ClassNotFoundException.class) + public void assure_before_method_of_second_test_is_executed_if_guaranteed_initialized() { + given().a_given_stage_with_guaranteed_field_initialized(); + then().the_before_is_not_called(); + } + + static class GuaranteedGivenTestStage extends Stage { + @ProvidedScenarioState(guaranteed = true) + Object guaranteedObject = null; + + GuaranteedGivenTestStage a_given_stage_with_guaranteed_field_uninitialized() { + return self(); + } + + GuaranteedGivenTestStage a_given_stage_with_guaranteed_field_initialized() { + this.guaranteedObject = "I'm initialized"; + return self(); + } + } + + static class GuaranteedWhenThenTestStage extends Stage { + @BeforeStage + public void beforeMethod() throws ClassNotFoundException { + throw new ClassNotFoundException("Not a JGivenMissingGuaranteedScenarioStateException"); + } + + GuaranteedWhenThenTestStage the_before_is_not_called() { + return self(); + } + } +} From 7716bbeed69e7167179e60f4d6887088df4bb7b8 Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Thu, 29 Jul 2021 13:02:20 +0100 Subject: [PATCH 05/11] Remove unnecessary throws statement Signed-off-by: Andru Stefanescu --- .../java/com/tngtech/jgiven/impl/inject/ValueInjector.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java index 37f148634d..d8de279992 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java @@ -140,8 +140,7 @@ private Object getValue(ScenarioStateField field) { return state.getValueByType(field.getField().getType()); } - private void checkGuaranteedStatesAreInitialized(Object instance) - throws JGivenMissingGuaranteedScenarioStateException { + private void checkGuaranteedStatesAreInitialized(Object instance) { for (Field field: FieldCache.get(instance.getClass()) .getFieldsWithAnnotation(ProvidedScenarioState.class, ScenarioState.class)) { if (field.isAnnotationPresent(ProvidedScenarioState.class)) { From 71542d7451fe9ad003730c2ab831f900ea34d193 Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Thu, 29 Jul 2021 13:06:04 +0100 Subject: [PATCH 06/11] Remove simple test Signed-off-by: Andru Stefanescu --- .../jgiven/impl/GuaranteedStateTest.java | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java deleted file mode 100644 index 385cf5d22a..0000000000 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.tngtech.jgiven.impl; - -import com.tngtech.jgiven.Stage; -import com.tngtech.jgiven.annotation.BeforeStage; -import com.tngtech.jgiven.annotation.ProvidedScenarioState; -import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; -import com.tngtech.jgiven.junit.ScenarioTest; -import org.junit.Test; - - -public class GuaranteedStateTest extends ScenarioTest { - - @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) - public void assure_before_method_of_second_test_is_executed_after_guaranteed_fields_validation() { - given().a_given_stage_with_guaranteed_field_uninitialized(); - then().the_before_is_not_called(); - } - - @Test(expected = ClassNotFoundException.class) - public void assure_before_method_of_second_test_is_executed_if_guaranteed_initialized() { - given().a_given_stage_with_guaranteed_field_initialized(); - then().the_before_is_not_called(); - } - - static class GuaranteedGivenTestStage extends Stage { - @ProvidedScenarioState(guaranteed = true) - Object guaranteedObject = null; - - GuaranteedGivenTestStage a_given_stage_with_guaranteed_field_uninitialized() { - return self(); - } - - GuaranteedGivenTestStage a_given_stage_with_guaranteed_field_initialized() { - this.guaranteedObject = "I'm initialized"; - return self(); - } - } - - static class GuaranteedWhenThenTestStage extends Stage { - @BeforeStage - public void beforeMethod() throws ClassNotFoundException { - throw new ClassNotFoundException("Not a JGivenMissingGuaranteedScenarioStateException"); - } - - GuaranteedWhenThenTestStage the_before_is_not_called() { - return self(); - } - } -} From 65e370f04038968e6a89619f969a2d0414c48fe3 Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Thu, 29 Jul 2021 15:34:39 +0100 Subject: [PATCH 07/11] Add new JGiven test for guaranteed field validator order Signed-off-by: Andru Stefanescu --- .../jgiven/impl/GuaranteedStateTest.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java new file mode 100644 index 0000000000..81e13358b7 --- /dev/null +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java @@ -0,0 +1,110 @@ +package com.tngtech.jgiven.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.BeforeStage; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; +import com.tngtech.jgiven.junit.ScenarioTest; +import com.tngtech.jgiven.junit.SimpleScenarioTest; +import com.tngtech.jgiven.report.model.ReportModel; +import com.tngtech.jgiven.testframework.TestExecutor; +import com.tngtech.jgiven.testframework.TestFramework; +import com.tngtech.jgiven.tests.TestScenarioRepository; +import org.junit.Test; + +public class GuaranteedStateTest extends SimpleScenarioTest { + + @Test + public void assure_before_method_of_second_test_is_executed_after_guaranteed_fields_validation() { + given().a_Jgiven_test_with_a_guaranteed_null_state(); + when().the_test_is_executed(); + then().the_report_contains_$_exception(JGivenMissingGuaranteedScenarioStateException.class); + } + + @Test + public void assure_before_method_of_second_test_is_executed_if_guaranteed_initialized() { + given().a_Jgiven_test_with_a_guaranteed_state(); + when().the_test_is_executed(); + then().the_report_contains_$_exception(ClassNotFoundException.class); + } + + public static class RealTest extends ScenarioTest { + + @Test + @SuppressWarnings("checkstyle:MethodName") + public void a_sample_test() { + given().a_sample_uninitialized_stage(); + when().I_do_something(); + then().I_did_something(); + } + + @Test + @SuppressWarnings("checkstyle:MethodName") + public void a_sample_initialized_test() { + given().a_sample_initialized_stage(); + when().I_do_something(); + then().I_did_something(); + } + + public static class RealGiven extends Stage { + @ProvidedScenarioState(guaranteed = true) + Object guaranteedObject = null; + + @SuppressWarnings("checkstyle:MethodName") + public void a_sample_uninitialized_stage() { + } + + @SuppressWarnings("checkstyle:MethodName") + public void a_sample_initialized_stage() { + this.guaranteedObject = "I'm initialized"; + } + } + + public static class RealThen extends Stage { + @SuppressWarnings("checkstyle:MethodName") + public void I_did_something() { + } + } + + public static class RealWhen extends Stage { + @BeforeStage + public void beforeSetup() throws ClassNotFoundException { + throw new ClassNotFoundException("Not a JGiven exception"); + } + + @SuppressWarnings("checkstyle:MethodName") + public void I_do_something() { + } + } + } + + public static class SimpleTestStage extends Stage { + TestScenarioRepository.TestScenario testScenario; + private ReportModel testReport; + + @SuppressWarnings("checkstyle:MethodName") + public void a_Jgiven_test_with_a_guaranteed_null_state() { + testScenario = new TestScenarioRepository.TestScenario(RealTest.class, "a_sample_test"); + } + + @SuppressWarnings("checkstyle:MethodName") + public void a_Jgiven_test_with_a_guaranteed_state() { + testScenario = new TestScenarioRepository.TestScenario(RealTest.class, "a_sample_initialized_test"); + } + + public void the_test_is_executed() { + testReport = TestExecutor.getExecutor(TestFramework.JUnit) + .execute(testScenario.testClass, testScenario.testMethod).getReportModel(); + } + + @SuppressWarnings("checkstyle:MethodName") + public void the_report_contains_$_exception(Class givenException) { + assertThat(testReport.getFailedScenarios()).isNotEmpty(); + assertThat(testReport.getFailedScenarios().get(0) + .getScenarioCases().get(0).getErrorMessage()).contains(givenException.getName()); + } + + } +} From 6d7e124e965f0120be5994c47254f4f0e21c962a Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Fri, 30 Jul 2021 12:36:51 +0100 Subject: [PATCH 08/11] Fix test class and minor changes Signed-off-by: Andru Stefanescu --- .../jgiven/impl/inject/ValueInjector.java | 6 +- .../jgiven/impl/inject/ValueInjectorTest.java | 37 +++++++++-- .../jgiven/tests/GuaranteedFieldRealTest.java | 51 +++++++++++++++ .../jgiven/impl/GuaranteedStateTest.java | 62 +------------------ 4 files changed, 91 insertions(+), 65 deletions(-) create mode 100644 jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GuaranteedFieldRealTest.java diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java index d8de279992..81b8da5809 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ValueInjector.java @@ -8,6 +8,7 @@ import com.tngtech.jgiven.annotation.ScenarioState; import com.tngtech.jgiven.annotation.ScenarioState.Resolution; import com.tngtech.jgiven.exception.AmbiguousResolutionException; +import com.tngtech.jgiven.exception.JGivenInjectionException; import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; import com.tngtech.jgiven.exception.JGivenMissingRequiredScenarioStateException; import com.tngtech.jgiven.impl.util.FieldCache; @@ -160,7 +161,10 @@ private void checkInitialized(Object instance, Field field) { Object value = null; try { value = field.get(instance); - } catch (IllegalAccessException e) { } + } catch (IllegalAccessException e) { + throw new JGivenInjectionException("The guaranteed field inside the scenario state cannot be accessed", + e); + } if (value == null) { throw new JGivenMissingGuaranteedScenarioStateException(field); } diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java index 730b92962d..e3c201db89 100644 --- a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java +++ b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java @@ -1,8 +1,10 @@ package com.tngtech.jgiven.impl.inject; +import com.tngtech.jgiven.annotation.ExpectedScenarioState; import com.tngtech.jgiven.annotation.ProvidedScenarioState; import com.tngtech.jgiven.annotation.ScenarioState; import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; +import com.tngtech.jgiven.exception.JGivenMissingRequiredScenarioStateException; import org.junit.Test; public class ValueInjectorTest { @@ -10,33 +12,58 @@ public class ValueInjectorTest { @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) public void null_provided_field_throws_exception() { - FakeStage stageObject = new FakeStage(null, ""); + FakeStage stageObject = new FakeStage(null, null, ""); injector.readValues(stageObject); } @Test(expected = JGivenMissingGuaranteedScenarioStateException.class) public void null_state_field_throws_exception() throws Throwable { - FakeStage stageObject = new FakeStage("", null); + FakeStage stageObject = new FakeStage("", null, null); injector.readValues(stageObject); } @Test public void initialized_fields_do_not_interrupt_execution() throws Throwable { - FakeStage stageObject = new FakeStage("", ""); + FakeStage stageObject = new FakeStage("", null, ""); injector.readValues(stageObject); } + @Test(expected = JGivenMissingRequiredScenarioStateException.class) + public void null_expected_field_throws_exception() { + FakeStage stageObject = new FakeStage(null, null, ""); + + injector.updateValues(stageObject); + } + + @Test(expected = JGivenMissingRequiredScenarioStateException.class) + public void null_expected_state_field_throws_exception() throws Throwable { + FakeStage stageObject = new FakeStage("", "", null); + + injector.updateValues(stageObject); + } + + @Test + public void initialized_expected_fields_do_not_interrupt_execution() throws Throwable { + FakeStage stageObject = new FakeStage("", "", ""); + + injector.readValues(stageObject); //update field value in cache + injector.updateValues(stageObject); + } + private class FakeStage { @ProvidedScenarioState(guaranteed = true) String providedObject; - @ScenarioState(guaranteed = true) + @ExpectedScenarioState(required = true) + String providedExpectedString; + @ScenarioState(guaranteed = true, required = true) String stateObject; - public FakeStage(String providedObject, String stateObject) { + public FakeStage(String providedObject, String providedExpectedString, String stateObject) { this.providedObject = providedObject; + this.providedExpectedString = providedExpectedString; this.stateObject = stateObject; } } diff --git a/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GuaranteedFieldRealTest.java b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GuaranteedFieldRealTest.java new file mode 100644 index 0000000000..4cf6fabd80 --- /dev/null +++ b/jgiven-tests/src/main/java/com/tngtech/jgiven/tests/GuaranteedFieldRealTest.java @@ -0,0 +1,51 @@ +package com.tngtech.jgiven.tests; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.BeforeStage; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import com.tngtech.jgiven.junit.ScenarioTest; +import org.junit.Test; + +public class GuaranteedFieldRealTest extends ScenarioTest { + + @Test + public void a_sample_test() { + given().a_sample_uninitialized_stage(); + when().I_do_something(); + then().I_did_something(); + } + + @Test + public void a_sample_initialized_test() { + given().a_sample_initialized_stage(); + when().I_do_something(); + then().I_did_something(); + } + + public static class RealGiven extends Stage { + @ProvidedScenarioState(guaranteed = true) + Object guaranteedObject = null; + + public void a_sample_uninitialized_stage() { + } + + public void a_sample_initialized_stage() { + this.guaranteedObject = "I'm initialized"; + } + } + + public static class RealThen extends Stage { + public void I_did_something() { + } + } + + public static class RealWhen extends Stage { + @BeforeStage + public void beforeSetup() throws ClassNotFoundException { + throw new ClassNotFoundException("Not a JGiven exception"); + } + + public void I_do_something() { + } + } +} diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java index 81e13358b7..6a36bca50e 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java @@ -3,14 +3,12 @@ import static org.assertj.core.api.Assertions.assertThat; import com.tngtech.jgiven.Stage; -import com.tngtech.jgiven.annotation.BeforeStage; -import com.tngtech.jgiven.annotation.ProvidedScenarioState; import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; -import com.tngtech.jgiven.junit.ScenarioTest; import com.tngtech.jgiven.junit.SimpleScenarioTest; import com.tngtech.jgiven.report.model.ReportModel; import com.tngtech.jgiven.testframework.TestExecutor; import com.tngtech.jgiven.testframework.TestFramework; +import com.tngtech.jgiven.tests.GuaranteedFieldRealTest; import com.tngtech.jgiven.tests.TestScenarioRepository; import org.junit.Test; @@ -30,68 +28,16 @@ public void assure_before_method_of_second_test_is_executed_if_guaranteed_initia then().the_report_contains_$_exception(ClassNotFoundException.class); } - public static class RealTest extends ScenarioTest { - - @Test - @SuppressWarnings("checkstyle:MethodName") - public void a_sample_test() { - given().a_sample_uninitialized_stage(); - when().I_do_something(); - then().I_did_something(); - } - - @Test - @SuppressWarnings("checkstyle:MethodName") - public void a_sample_initialized_test() { - given().a_sample_initialized_stage(); - when().I_do_something(); - then().I_did_something(); - } - - public static class RealGiven extends Stage { - @ProvidedScenarioState(guaranteed = true) - Object guaranteedObject = null; - - @SuppressWarnings("checkstyle:MethodName") - public void a_sample_uninitialized_stage() { - } - - @SuppressWarnings("checkstyle:MethodName") - public void a_sample_initialized_stage() { - this.guaranteedObject = "I'm initialized"; - } - } - - public static class RealThen extends Stage { - @SuppressWarnings("checkstyle:MethodName") - public void I_did_something() { - } - } - - public static class RealWhen extends Stage { - @BeforeStage - public void beforeSetup() throws ClassNotFoundException { - throw new ClassNotFoundException("Not a JGiven exception"); - } - - @SuppressWarnings("checkstyle:MethodName") - public void I_do_something() { - } - } - } - public static class SimpleTestStage extends Stage { TestScenarioRepository.TestScenario testScenario; private ReportModel testReport; - @SuppressWarnings("checkstyle:MethodName") public void a_Jgiven_test_with_a_guaranteed_null_state() { - testScenario = new TestScenarioRepository.TestScenario(RealTest.class, "a_sample_test"); + testScenario = new TestScenarioRepository.TestScenario(GuaranteedFieldRealTest.class, "a_sample_test"); } - @SuppressWarnings("checkstyle:MethodName") public void a_Jgiven_test_with_a_guaranteed_state() { - testScenario = new TestScenarioRepository.TestScenario(RealTest.class, "a_sample_initialized_test"); + testScenario = new TestScenarioRepository.TestScenario(GuaranteedFieldRealTest.class, "a_sample_initialized_test"); } public void the_test_is_executed() { @@ -99,12 +45,10 @@ public void the_test_is_executed() { .execute(testScenario.testClass, testScenario.testMethod).getReportModel(); } - @SuppressWarnings("checkstyle:MethodName") public void the_report_contains_$_exception(Class givenException) { assertThat(testReport.getFailedScenarios()).isNotEmpty(); assertThat(testReport.getFailedScenarios().get(0) .getScenarioCases().get(0).getErrorMessage()).contains(givenException.getName()); } - } } From 589fb26bdf5babd15c849bbf1d3c561f471196b3 Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Fri, 30 Jul 2021 19:22:37 +0100 Subject: [PATCH 09/11] Update changelog Signed-off-by: Andru Stefanescu --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d94bda6391..f738e8c188 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # v1.1 ##New features * The lifecycle method annotations `@BeforeStage` and `@AfterStage` have an option to make the associated methods be invoked repeatedly if the stage class is invoked multiple times. Note that the duration of a stage is denoted by the change of the stage class, thus multiple invocations (no matter the form) on the same stage class count as one stage. +* The field annotations `@ScenarioState` and `@ProvidedScenarioState` can be set to be `guaranteed`. This ensures that the stage that is declaring such field must operate on it during execution and after the stage finishes the field is ensured to be initialized. ##Fixed Issues * Refurbished the jgiven-scala example project [#619](https://github.com/TNG/JGiven/pull/619) (thanks to seblm) From a27c79e4c71f86534624cc2f105b44984132f7c5 Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Fri, 30 Jul 2021 19:26:14 +0100 Subject: [PATCH 10/11] Fix changelog spacing Signed-off-by: Andru Stefanescu --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f738e8c188..1725940815 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ # v1.1 -##New features +## New features * The lifecycle method annotations `@BeforeStage` and `@AfterStage` have an option to make the associated methods be invoked repeatedly if the stage class is invoked multiple times. Note that the duration of a stage is denoted by the change of the stage class, thus multiple invocations (no matter the form) on the same stage class count as one stage. * The field annotations `@ScenarioState` and `@ProvidedScenarioState` can be set to be `guaranteed`. This ensures that the stage that is declaring such field must operate on it during execution and after the stage finishes the field is ensured to be initialized. -##Fixed Issues +## Fixed Issues * Refurbished the jgiven-scala example project [#619](https://github.com/TNG/JGiven/pull/619) (thanks to seblm) * Updated most dependencies in the project * Enabled printing of nested stage [#366](https://github.com/TNG/JGiven/pull/619) (thanks to laurinvesely) From 2cd8232301714e89a2123c914a7fe1c3e841163a Mon Sep 17 00:00:00 2001 From: Andru Stefanescu Date: Mon, 2 Aug 2021 15:33:17 +0100 Subject: [PATCH 11/11] Minor changes Signed-off-by: Andru Stefanescu --- .../tngtech/jgiven/impl/StageLifecycleManager.java | 5 ----- .../tngtech/jgiven/impl/inject/ValueInjectorTest.java | 11 ++++++++--- .../com/tngtech/jgiven/impl/GuaranteedStateTest.java | 11 ++++++++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java index 5bb86a1b6e..6fd930eb80 100644 --- a/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java +++ b/jgiven-core/src/main/java/com/tngtech/jgiven/impl/StageLifecycleManager.java @@ -4,15 +4,10 @@ import com.tngtech.jgiven.annotation.AfterStage; import com.tngtech.jgiven.annotation.BeforeScenario; import com.tngtech.jgiven.annotation.BeforeStage; -import com.tngtech.jgiven.annotation.ProvidedScenarioState; -import com.tngtech.jgiven.annotation.ScenarioState; -import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; import com.tngtech.jgiven.exception.JGivenUserException; import com.tngtech.jgiven.impl.intercept.StepInterceptorImpl; -import com.tngtech.jgiven.impl.util.FieldCache; import com.tngtech.jgiven.impl.util.ReflectionUtil; import java.lang.annotation.Annotation; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java index e3c201db89..5a25dca078 100644 --- a/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java +++ b/jgiven-core/src/test/java/com/tngtech/jgiven/impl/inject/ValueInjectorTest.java @@ -1,5 +1,7 @@ package com.tngtech.jgiven.impl.inject; +import static org.assertj.core.api.Assertions.assertThat; + import com.tngtech.jgiven.annotation.ExpectedScenarioState; import com.tngtech.jgiven.annotation.ProvidedScenarioState; import com.tngtech.jgiven.annotation.ScenarioState; @@ -25,7 +27,7 @@ public void null_state_field_throws_exception() throws Throwable { } @Test - public void initialized_fields_do_not_interrupt_execution() throws Throwable { + public void initialized_fields_do_not_interrupt_execution() { FakeStage stageObject = new FakeStage("", null, ""); injector.readValues(stageObject); @@ -39,18 +41,21 @@ public void null_expected_field_throws_exception() { } @Test(expected = JGivenMissingRequiredScenarioStateException.class) - public void null_expected_state_field_throws_exception() throws Throwable { + public void null_expected_state_field_throws_exception() { FakeStage stageObject = new FakeStage("", "", null); injector.updateValues(stageObject); } @Test - public void initialized_expected_fields_do_not_interrupt_execution() throws Throwable { + public void initialized_expected_fields_do_not_interrupt_execution() { FakeStage stageObject = new FakeStage("", "", ""); injector.readValues(stageObject); //update field value in cache + injector.injectValueByName("providedExpectedString", "Test"); injector.updateValues(stageObject); + + assertThat(stageObject.providedExpectedString).isEqualTo("Test"); } private class FakeStage { diff --git a/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java index 6a36bca50e..fbd7176f43 100644 --- a/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java +++ b/jgiven-tests/src/test/java/com/tngtech/jgiven/impl/GuaranteedStateTest.java @@ -6,6 +6,7 @@ import com.tngtech.jgiven.exception.JGivenMissingGuaranteedScenarioStateException; import com.tngtech.jgiven.junit.SimpleScenarioTest; import com.tngtech.jgiven.report.model.ReportModel; +import com.tngtech.jgiven.testframework.TestExecutionResult; import com.tngtech.jgiven.testframework.TestExecutor; import com.tngtech.jgiven.testframework.TestFramework; import com.tngtech.jgiven.tests.GuaranteedFieldRealTest; @@ -37,12 +38,16 @@ public void a_Jgiven_test_with_a_guaranteed_null_state() { } public void a_Jgiven_test_with_a_guaranteed_state() { - testScenario = new TestScenarioRepository.TestScenario(GuaranteedFieldRealTest.class, "a_sample_initialized_test"); + testScenario = new TestScenarioRepository.TestScenario(GuaranteedFieldRealTest.class, + "a_sample_initialized_test"); } public void the_test_is_executed() { - testReport = TestExecutor.getExecutor(TestFramework.JUnit) - .execute(testScenario.testClass, testScenario.testMethod).getReportModel(); + TestExecutor testExecutor = TestExecutor.getExecutor(TestFramework.JUnit); + TestExecutionResult testExecutionResult = testExecutor.execute(testScenario.testClass, + testScenario.testMethod); + + testReport = testExecutionResult.getReportModel(); } public void the_report_contains_$_exception(Class givenException) {