forked from TNG/JGiven
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added required attribute to ScenarioState.
This commit adds a new boolean attribute "required" to both ScenarioState and ExpectedScenarioState. If set to true on a field within a stage, corresponding tests will fail automatically if the state hasn't been provided. fixes TNG#255
- Loading branch information
Showing
7 changed files
with
243 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
...c/main/java/com/tngtech/jgiven/exception/JGivenMissingRequiredScenarioStateException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.tngtech.jgiven.exception; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
/** | ||
* This exception is thrown if a scenario state has been marked as required, | ||
* but the state hasn't been provided. | ||
*/ | ||
public class JGivenMissingRequiredScenarioStateException extends RuntimeException { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public JGivenMissingRequiredScenarioStateException( Field field ) { | ||
super( "The field " + field.getName() + " is required but has not been provided." ); | ||
} | ||
|
||
} |
105 changes: 105 additions & 0 deletions
105
jgiven-core/src/main/java/com/tngtech/jgiven/impl/inject/ScenarioStateField.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package com.tngtech.jgiven.impl.inject; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.Field; | ||
|
||
import com.tngtech.jgiven.annotation.ExpectedScenarioState; | ||
import com.tngtech.jgiven.annotation.ProvidedScenarioState; | ||
import com.tngtech.jgiven.annotation.ScenarioState; | ||
import com.tngtech.jgiven.annotation.ScenarioState.Resolution; | ||
import com.tngtech.jgiven.relocated.guava.base.Function; | ||
|
||
/** | ||
* Used internally to avoid repeated annotation lookups. | ||
*/ | ||
final class ScenarioStateField { | ||
|
||
public static Function<Field, ScenarioStateField> fromField = new Function<Field, ScenarioStateField>() { | ||
@Override | ||
public ScenarioStateField apply( Field field ) { | ||
return new ScenarioStateField( field ); | ||
} | ||
}; | ||
|
||
private final Field field; | ||
|
||
private Resolution declaredResolution; | ||
private boolean required; | ||
|
||
private ScenarioStateField( Field field ) { | ||
this.field = field; | ||
|
||
collectAnnotations( field ); | ||
if( declaredResolution == null ) { | ||
throw new IllegalArgumentException( "Field " + field + " has no valid annotation" ); | ||
} | ||
} | ||
|
||
public Field getField() { | ||
return field; | ||
} | ||
|
||
/** | ||
* Return the {@link Resolution} defined for this state. | ||
*/ | ||
public Resolution getResolution() { | ||
if( declaredResolution == Resolution.AUTO ) { | ||
return typeIsTooGeneric( field.getType() ) ? Resolution.NAME : Resolution.TYPE; | ||
} | ||
|
||
return declaredResolution; | ||
} | ||
|
||
/** | ||
* Returns {@code true} if and only if the {@link Required} annotation is present on this state. | ||
*/ | ||
public boolean isRequired() { | ||
return required; | ||
} | ||
|
||
private void collectAnnotations( Field field ) { | ||
for( Annotation annotation : field.getAnnotations() ) { | ||
if( declaredResolution == null ) { | ||
declaredResolution = collectDeclaredResolution( annotation ); | ||
} | ||
|
||
required |= collectRequired( annotation ); | ||
} | ||
} | ||
|
||
private Resolution collectDeclaredResolution( Annotation annotation ) { | ||
if( annotation instanceof ScenarioState ) { | ||
return ( (ScenarioState) annotation ).resolution(); | ||
} | ||
|
||
if( annotation instanceof ProvidedScenarioState ) { | ||
return ( (ProvidedScenarioState) annotation ).resolution(); | ||
} | ||
|
||
if( annotation instanceof ExpectedScenarioState ) { | ||
return ( (ExpectedScenarioState) annotation ).resolution(); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private boolean collectRequired( Annotation annotation ) { | ||
if( annotation instanceof ScenarioState ) { | ||
return ( (ScenarioState) annotation ).required(); | ||
} | ||
|
||
if( annotation instanceof ExpectedScenarioState ) { | ||
return ( (ExpectedScenarioState) annotation ).required(); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private boolean typeIsTooGeneric( Class<?> type ) { | ||
return type.isPrimitive() | ||
|| type.getName().startsWith( "java.lang" ) | ||
|| type.getName().startsWith( "java.io" ) | ||
|| type.getName().startsWith( "java.util" ); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
jgiven-junit/src/test/java/com/tngtech/jgiven/junit/RequiredScenarioStateTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package com.tngtech.jgiven.junit; | ||
|
||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import com.tngtech.java.junit.dataprovider.DataProviderRunner; | ||
import com.tngtech.jgiven.annotation.ExpectedScenarioState; | ||
import com.tngtech.jgiven.annotation.JGivenConfiguration; | ||
import com.tngtech.jgiven.annotation.ScenarioState; | ||
import com.tngtech.jgiven.exception.JGivenMissingRequiredScenarioStateException; | ||
import com.tngtech.jgiven.junit.test.BeforeAfterTestStage; | ||
import com.tngtech.jgiven.junit.test.ThenTestStep; | ||
import com.tngtech.jgiven.junit.test.WhenTestStep; | ||
|
||
@RunWith( DataProviderRunner.class ) | ||
@JGivenConfiguration( TestConfiguration.class ) | ||
public class RequiredScenarioStateTest extends ScenarioTest<BeforeAfterTestStage, WhenTestStep, ThenTestStep> { | ||
|
||
static class StageWithMissingScenarioState { | ||
@ScenarioState( required = true ) | ||
Boolean state; | ||
|
||
public void something() {} | ||
} | ||
|
||
@Test( expected = JGivenMissingRequiredScenarioStateException.class ) | ||
public void required_states_must_be_present() throws Throwable { | ||
StageWithMissingScenarioState stage = addStage( StageWithMissingScenarioState.class ); | ||
stage.something(); | ||
} | ||
|
||
static class StageWithMissingExpectedScenarioState { | ||
@ExpectedScenarioState( required = true ) | ||
Boolean state; | ||
|
||
public void something() {} | ||
} | ||
|
||
@Test( expected = JGivenMissingRequiredScenarioStateException.class ) | ||
public void required__expected_states_must_be_present() throws Throwable { | ||
StageWithMissingExpectedScenarioState stage = addStage( StageWithMissingExpectedScenarioState.class ); | ||
stage.something(); | ||
} | ||
|
||
static class ProviderStage { | ||
@ScenarioState | ||
Boolean state; | ||
|
||
public void provide() { | ||
this.state = true; | ||
} | ||
} | ||
|
||
@Test | ||
public void scenarios_pass_if_required_state_is_provided_by_another_stage() throws Throwable { | ||
ProviderStage stage = addStage( ProviderStage.class ); | ||
StageWithMissingScenarioState stage2 = addStage( StageWithMissingScenarioState.class ); | ||
|
||
stage.provide(); | ||
stage2.something(); | ||
} | ||
|
||
} |