Skip to content
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

Use Spring's @Rule support instead of @RunWith(SpringJunit4ClassRunner) #250

Merged
merged 1 commit into from
Nov 14, 2016
Merged
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
Original file line number Diff line number Diff line change
@@ -1,30 +1,18 @@
package com.tngtech.jgiven.examples.pancakes.test;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.tngtech.jgiven.examples.pancakes.app.SpringConfig;
import com.tngtech.jgiven.examples.pancakes.test.steps.GivenIngredients;
import com.tngtech.jgiven.examples.pancakes.test.steps.ThenMeal;
import com.tngtech.jgiven.examples.pancakes.test.steps.WhenCook;
import com.tngtech.jgiven.integration.spring.SpringCanWire;
import com.tngtech.jgiven.junit.ScenarioTest;
import com.tngtech.jgiven.integration.spring.SpringScenarioTest;

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( classes = SpringConfig.class )
public class SpringPanCakeScenarioTest extends ScenarioTest<GivenIngredients, WhenCook, ThenMeal> {
@Autowired
private AutowireCapableBeanFactory beanFactory;

@Before
public void setupSpring() {
wireSteps( new SpringCanWire( beanFactory ) );
}
@ContextConfiguration( classes = TestSpringConfig.class )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, thanks for adapting that!

public class SpringPanCakeScenarioTest extends SpringScenarioTest<GivenIngredients, WhenCook, ThenMeal> {

@Test
public void a_pancake_can_be_fried_out_of_an_egg_milk_and_flour() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.tngtech.jgiven.examples.pancakes.test;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import com.tngtech.jgiven.examples.pancakes.app.SpringConfig;
import com.tngtech.jgiven.integration.spring.EnableJGiven;

@Configuration
@EnableJGiven
@Import( value = SpringConfig.class )
@ComponentScan( basePackages = "com.tngtech.jgiven.examples.pancakes.test")
public class TestSpringConfig {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;
import java.util.Set;

import com.tngtech.jgiven.integration.spring.JGivenStage;
import org.springframework.beans.factory.annotation.Autowired;

import com.tngtech.jgiven.Stage;
Expand All @@ -14,6 +15,7 @@
import com.tngtech.jgiven.examples.pancakes.app.Cook;

//tag::state[]
@JGivenStage
public class WhenCook extends Stage<WhenCook> {
@Autowired
@ScenarioState
Expand Down
3 changes: 2 additions & 1 deletion jgiven-spring/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ description = "Module for using Spring dependency injection together with JGiven
dependencies {
compile project(':jgiven-core')
compileOnly(group: 'org.springframework', name: 'spring-context', version: springVersion)
compileOnly(group: 'org.springframework', name: 'spring-test', version: springVersion)
compile project(':jgiven-junit')
testCompile group: 'org.springframework', name: 'spring-test', version: springVersion
testCompile(group: 'org.springframework', name: 'spring-context', version: springVersion)
testCompile(group: 'org.springframework', name: 'spring-test', version: springVersion)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.tngtech.jgiven.integration.spring;

import org.junit.ClassRule;
import org.junit.Rule;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;

import com.tngtech.jgiven.base.SimpleScenarioTestBase;
import com.tngtech.jgiven.impl.ScenarioExecutor;

/**
* Internal class necessary in order to provide the correct ordering of the {@link org.junit.rules.MethodRule}s. Must be public because of
* {@link SpringMethodRule}s validations.
* It should not be used directly. Instead, use {@link SimpleSpringRuleScenarioTest}.
*
* @param <STAGE>
*
* @since 0.14.0
*/
public abstract class InternalSimpleSpringScenarioTest<STAGE> extends SimpleScenarioTestBase<STAGE> implements BeanFactoryAware {

@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();

@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();

InternalSimpleSpringScenarioTest() {
}

public void setBeanFactory(BeanFactory beanFactory) {
this.getScenario().setExecutor((ScenarioExecutor)beanFactory.getBean( SpringScenarioExecutor.class ));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.tngtech.jgiven.integration.spring;

import org.junit.ClassRule;
import org.junit.Rule;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;

import com.tngtech.jgiven.base.ScenarioTestBase;
import com.tngtech.jgiven.impl.ScenarioExecutor;

/**
* Internal class necessary in order to provide the correct ordering of the {@link org.junit.rules.MethodRule}s. Must be public because of
* {@link SpringMethodRule}s validations.
* It should not be used directly. Instead, use {@link SpringRuleScenarioTest}.
*
* @param <GIVEN>
* @param <WHEN>
* @param <THEN>
*
* @since 0.14.0
*/
public abstract class InternalSpringScenarioTest<GIVEN, WHEN, THEN> extends ScenarioTestBase<GIVEN, WHEN, THEN> implements BeanFactoryAware {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the constructor is already package-private you could also make the class itself package-private

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this does not work because of Springs ClassRule:
org.junit.internal.runners.rules.ValidationError: The @ClassRule 'springClassRule' must be declared in a public class.
Would you think it's more consistent to have the constructor public then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And move it to a package named "internal"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no I think that would be overkill. The class is visible in any case, so just leave it as it is


@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();

@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();

InternalSpringScenarioTest() {
}

public void setBeanFactory(BeanFactory beanFactory) {
this.getScenario().setExecutor((ScenarioExecutor)beanFactory.getBean( SpringScenarioExecutor.class ));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.tngtech.jgiven.integration.spring;

import com.tngtech.jgiven.impl.Scenario;
import com.tngtech.jgiven.junit.ScenarioExecutionRule;
import com.tngtech.jgiven.junit.ScenarioReportRule;
import org.junit.ClassRule;
import org.junit.Rule;

/**
* A variant of {@link SpringRuleScenarioTest} works with a single
* stage type parameter instead of three.
*
* @param <STEPS> the stage class that contains the step definitions
*
* @since 0.14.0
*/
public class SimpleSpringRuleScenarioTest<STEPS> extends InternalSimpleSpringScenarioTest<STEPS> {

@ClassRule
public static final ScenarioReportRule writerRule = new ScenarioReportRule();

@Rule
public final ScenarioExecutionRule scenarioRule = new ScenarioExecutionRule( createScenario() );

@Override
public Scenario<STEPS, STEPS, STEPS> getScenario() {
return (Scenario<STEPS, STEPS, STEPS>) scenarioRule.getScenario();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.tngtech.jgiven.integration.spring;

import org.junit.ClassRule;
import org.junit.Rule;

import com.tngtech.jgiven.impl.Scenario;
import com.tngtech.jgiven.junit.ScenarioExecutionRule;
import com.tngtech.jgiven.junit.ScenarioReportRule;

/**
* Base class for {@link SpringScenarioExecutor} based JGiven tests
*
* Uses JUnit rules (introduced in Spring 4.2) instead of a JUnit runner in
* order to allow custom JUnit runners.
*
*
* @param <GIVEN>
* @param <WHEN>
* @param <THEN>
*
* @since 0.14.0
*/
public class SpringRuleScenarioTest<GIVEN, WHEN, THEN> extends InternalSpringScenarioTest<GIVEN, WHEN, THEN> {

@ClassRule
public static final ScenarioReportRule writerRule = new ScenarioReportRule();

@Rule
public final ScenarioExecutionRule scenarioRule = new ScenarioExecutionRule( createScenario() );

@Override
public Scenario<GIVEN, WHEN, THEN> getScenario() {
return (Scenario<GIVEN, WHEN, THEN>) scenarioRule.getScenario();
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package com.tngtech.jgiven.integration.spring;

import com.tngtech.jgiven.junit.ScenarioTest;
import org.junit.ClassRule;
import org.junit.Rule;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

import com.tngtech.jgiven.base.ScenarioTestBase;
import com.tngtech.jgiven.junit.ScenarioExecutionRule;
import com.tngtech.jgiven.junit.ScenarioReportRule;
import com.tngtech.jgiven.junit.ScenarioTest;

/**
* Base class for {@link SpringScenarioExecutor} based JGiven tests
* Base class for {@link SpringScenarioExecutor} based JGiven tests.
*
* Needs to be used with the {@link org.springframework.test.context.junit4.SpringJUnit4ClassRunner}.
* As a JUnit-rule based alternative, consider using {@link SpringRuleScenarioTest}.
*
* @param <GIVEN>
* @param <WHEN>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.tngtech.jgiven.integration.spring.test;

import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;

import com.tngtech.jgiven.integration.spring.SimpleSpringRuleScenarioTest;
import com.tngtech.jgiven.integration.spring.config.TestSpringConfig;

@ContextConfiguration( classes = TestSpringConfig.class )
public class SimpleSpringRuleScenarioTestTest extends SimpleSpringRuleScenarioTest<SimpleTestSpringSteps> {

@Test
public void spring_can_inject_beans_into_stages() {
given().a_step_that_is_a_spring_component();
when().methods_on_this_component_are_called();
then().beans_are_injected();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public SimpleTestSpringSteps methods_on_this_component_are_called() {
return this;
}

public SimpleTestSpringSteps method_with_parameter_is_called(String message) {
testBean.sayHello(message);
return this;
}

public void beans_are_injected() {
Assertions.assertThat( testBean ).isNotNull();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.tngtech.jgiven.integration.spring.test;

import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;

import com.tngtech.jgiven.integration.spring.SpringRuleScenarioTest;
import com.tngtech.jgiven.integration.spring.config.TestSpringConfig;

@ContextConfiguration( classes = TestSpringConfig.class )
public class SpringRuleScenarioTestTest extends SpringRuleScenarioTest<SimpleTestSpringSteps, SimpleTestSpringSteps, SimpleTestSpringSteps> {

@Test
public void spring_can_inject_beans_into_stages() {
given().a_step_that_is_a_spring_component();
when().methods_on_this_component_are_called();
then().beans_are_injected();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.tngtech.jgiven.integration.spring.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;

import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.jgiven.integration.spring.SpringRuleScenarioTest;
import com.tngtech.jgiven.integration.spring.config.TestSpringConfig;

@RunWith( DataProviderRunner.class )
@ContextConfiguration( classes = TestSpringConfig.class )
public class SpringRuleScenarioWithDataProviderTest extends SpringRuleScenarioTest<SimpleTestSpringSteps, SimpleTestSpringSteps, SimpleTestSpringSteps> {

@Test
@DataProvider({"John", "Doe"})
public void spring_can_inject_beans_into_stages(String name) {
given().a_step_that_is_a_spring_component();
when().method_with_parameter_is_called(name);
then().beans_are_injected();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ public class TestBean {
public String computeSomething() {
return "result";
}

public String sayHello(String message) {
return "Hello " + message;
}
}