web-dev-qa-db-fra.com

Concombre avec Spring Boot 1.4: Dépendances non injectées lors de l'utilisation de @SpringBootTest et @RunWith (SpringRunner.class)

J'écris une nouvelle application et j'essaie de faire du BDD avec concombre et Spring Boot 1.4. Le code de travail est comme indiqué ci-dessous:

@SpringBootApplication
public class Application {
    @Bean
    MyService myService() {
        return new MyService();
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

public class MyService {}

Le code de test est comme indiqué ci-dessous:

@RunWith(Cucumber.class)
public class RunFeatures {}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)
public class MyStepDef {
    @Autowired
    MyService myService;

    @Given("^Some initial condition$")
    public void appIsStarted() throws Throwable {
        if (service == null) throw new Exception("Dependency not injected!");
        System.out.println("App started");
    }

    @Then("^Nothing happens$")
    public void thereShouldBeNoException() throws Throwable {
        System.out.println("Test passed");
    }
}

Le fichier de fonctionnalités est comme indiqué ci-dessous:

Feature: Test Cucumber with spring
    Scenario: First Scenario
        Given Some initial condition
        Then Nothing happens

Lorsque j'exécute le programme ci-dessus tel quel, tout fonctionne correctement et la dépendance (MyService) est injectée dans MyStepDef sans aucun problème.

Si je remplace ce code:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)

Avec le code ci-dessous (Nouvelle façon de le gérer dans Spring Boot 1.4):

@RunWith(SpringRunner.class)
@SpringBootTest

Ensuite, la dépendance (MyService) n'est jamais injectée. Est-ce que je manque quelque chose peut-être?

Merci d'avance pour votre aide!!!

11
Aliyu Fonyuy

J'ai eu le même problème. Le commentaire ci-dessus m'a dirigé vers la solution

Le code problématique dans concombre-printemps semble être celui-ci github.com/cucumber/cucumber-jvm/blob/master/spring/src/main‌ /… 

Après avoir ajouté l'annotation @ContextConfiguration, les tests fonctionnent comme prévu.

Donc, ce que j'ai est la suivante ...

@RunWith(Cucumber.class)
@CucumberOptions(plugin = {"json:target/cucumber.json", "pretty"}, features = "src/test/features")
public class CucumberTest {
}

@ContextConfiguration
@SpringBootTest
public abstract class StepDefs {
}

public class MyStepDefs extends StepDefs {

    @Inject
    Service service;

    @Inject
    Repository repository;

    [...]

}

J'espère que cela vous aide plus loin

15
stripfire

Je l'ai eu avec Spring Boot 1.5.x et 2.0 puis a écrit un article de blog pour tenter de clarifier cela, car c'est délicat.

Premièrement, même si c'est évident, vous devez avoir les bonnes dépendances incluses dans votre projet (étant cucumber-spring l'important ici). Par exemple, avec Maven:

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-Java</artifactId>
    <version>2.3.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>2.3.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-spring</artifactId>
    <version>2.3.1</version>
    <scope>test</scope>
</dependency>

Maintenant, la partie importante pour que cela fonctionne, résume: 

  • Le point d’entrée de votre test doit être une classe annotée avec @RunWith(Cucumber.class
  • Cette classe utilisera les définitions d'étapes, qui sont normalement dans une classe séparée avec des méthodes annotées (@Given, @When, @Then, etc.). 
  • L'astuce est que cette classe doit étendre une classe de base annotée avec @SpringBootTest, @RunWith(SpringRunner.class) et toute autre configuration nécessaire pour exécuter votre test avec Spring Boot. Par exemple, si vous implémentez un test d'intégration sans vous moquer d'autres couches, vous devez ajouter la configuration webEnvironment et la définir sur RANDOM_PORT ou DEFINED_PORT

Voir le schéma et le squelette de code ci-dessous.

 TPD - Using DI with Cucumber in Spring Boot

Le point d'entrée:

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources/features/bag.feature", plugin = {"pretty", "html:target/cucumber"})
public class BagCucumberIntegrationTest {
}

La classe de test de base Spring Boot:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class SpringBootBaseIntegrationTest {
}

La classe des définitions d'étape: 

@Ignore
public class BagCucumberStepDefinitions extends SpringBootBaseIntegrationTest {
  // @Given, @When, @Then annotated methods
}

C’est ce dont vous avez besoin pour faire fonctionner l’ID. Pour un exemple de code complet, vérifiez simplement my blog post ou le code dans GitHub

4
Moisés

Avant Spring Boot 1.4, vous pouvez utiliser 

@ContextConfiguration(classes = {YourSpringConfiguration.class}, loader = SpringApplicationContextLoader.class)

À partir de Spring Boot 1.4, SpringApplicationContextLoader est obsolète. Vous devez donc utiliser SpringBootContextLoader.class à la place.

Ajouter simplement @SpringBootTest (avec une classe de configuration facultative) devrait fonctionner de manière autonome, mais si vous regardez le code dans cucumber.runtime.Java.spring.SpringFactory, la méthode annotatedWithSupportedSpringRootTestAnnotations ne vérifie pas cette annotation, ce qui explique pourquoi Une annotation en conjonction avec @SpringBootTest fonctionne.

En réalité, le code concombre-printemps doit changer. Je vais voir si je peux soulever un problème, car dans la documentation de Spring, il est indiqué que SpringApplicationContextLoader ne doit être utilisé qu'en cas d'absolue nécessité. Je vais essayer de soulever un problème à ce sujet pour le support de ressort de concombre.

La réponse de stripwire en combinant @SpringBootTest et @ContextConfiguration est donc la meilleure solution de contournement.

1
PaulNUK

Je l'ai travailler dans Spring Boot 1.5. Je veux partager la configuration avec vous:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.Apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        ...
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

        ...
    <dependencies>
            ...
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-Java</artifactId>
            <version>1.2.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-spring</artifactId>
            <version>1.2.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>1.2.5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
        ...
</project>

Fichier de caractéristiques

Feature: User should be greeted

  Background:
    Given The database is empty
    Then All connections are set

  Scenario: Default user is greeted
    Given A default user
    When The application is started
    Then The user should be greeted with "Hello Marc!"

Crochet de concombre

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources", strict = true)
public class CucumberTests {    // Classname should end on *Tests

}

Résumé de configuration de printemps

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration
abstract class AbstractSpringConfigurationTest {

}

La colle

class CucumberGlue : AbstractSpringConfigurationTest() {

    @Autowired
    lateinit var restTemplate: TestRestTemplate

    @Autowired
    lateinit var restController: RestController

    @Autowired
    lateinit var personRepository: PersonRepository

    @Autowired
    lateinit var entityManager: EntityManager

    private var result: String? = null

    @Given("^The database is empty$")
    fun the_database_is_empty() {
        personRepository.deleteAll()
    }

    @Then("^All connections are set$")
    fun all_connections_are_set() {
        assertThat(restTemplate).isNotNull()
        assertThat(entityManager).isNotNull()
    }

    @Given("^A default user$")
    fun a_default_user() {
    }

    @When("^The application is started$")
    fun the_application_is_started() {
        result = restController.testGet()
    }

    @Then("^The user should be greeted with \"([^\"]*)\"$")
    fun the_user_should_be_greeted_with(expectedName: String) {
        assertThat(result).isEqualTo(expectedName)
    }

}
0
Marc Enschede

Ceci est ma configuration, vous pouvez l'essayer

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ContextConfiguration(classes = {Application.class})
0
howie