Comment définir une variable d'environnement ou une propriété système dans les tests de printemps?

93

J'aimerais écrire quelques tests qui vérifient la configuration XML Spring d'un WAR déployé. Malheureusement, certains beans nécessitent que certaines variables d'environnement ou propriétés système soient définies. Comment puis-je définir une variable d'environnement avant l'initialisation des beans spring lors de l'utilisation du style de test pratique avec @ContextConfiguration?

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:whereever/context.xml")
public class TestWarSpringContext { ... }

Si je configure le contexte de l'application avec des annotations, je ne vois pas de hook où je peux faire quelque chose avant que le contexte de printemps ne soit initialisé.

Hans-Peter Störr
la source

Réponses:

127

Vous pouvez initialiser la propriété System dans un initialiseur statique:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:whereever/context.xml")
public class TestWarSpringContext {

    static {
        System.setProperty("myproperty", "foo");
    }

}

Le code d'initialisation statique sera exécuté avant l'initialisation du contexte de l'application Spring.

Jimmy Praet
la source
14
Silly moi - OK, cela fonctionnerait. Mieux encore: probablement une @BeforeClassméthode pour définir la propriété système et une @AfterClassméthode pour la supprimer fonctionnerait également et se nettoierait bien après elle-même. (Je n'ai pas essayé, cependant.)
Hans-Peter Störr
1
J'ai essayé le @BeforeClass - et cela a bien fonctionné pour définir les propriétés système avant que d'autres propriétés ne soient définies dans l'instance de test
wbdarby le
Merci pour cela. La chose statique n'a pas fonctionné mais une petite méthode avec @BeforeClass a fonctionné!
Midhun Agnihotram
Ce mécanisme ne fonctionne pas si vous modifiez la propriété du fichier de configuration Log4j2. Il semble que Spring de toute façon est en cours de chargement (et donc de journalisation incorrecte) avant ce morceau de code statique.
lucasvc
86

La bonne façon de faire cela, à partir de Spring 4.1, est d'utiliser une @TestPropertySourceannotation.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:whereever/context.xml")
@TestPropertySource(properties = {"myproperty = foo"})
public class TestWarSpringContext {
    ...    
}

Voir @TestPropertySource dans les documents Spring et Javadocs .

Raman
la source
2
Cette annotation prend également en charge un chemin de fichier de propriétés.
MigDus
2
Je pourrais changer l'étiquette du client Spring Cloud Config pendant les tests en utilisant@TestPropertySource(properties={"spring.cloud.config.label=feature/branch"})
Marcello de Sales
4
Bonne réponse, mais malheureusement ne fonctionnait pas pour moi, en utilisant Spring 4.2.9, la propriété était toujours vide. Seul le bloc statique fonctionnait ... A fonctionné pour les propriétés de l'application, mais pas pour les propriétés du système.
Gregor
J'ai d'abord vu et essayé la version statique (qui a fonctionné), mais cette annotation est encore plus propre et beaucoup plus préférable (pour moi, car elle fonctionne aussi comme un charme).
BAERUS
3
Cela fournit une Environmentpropriété, qui est différente d'une "variable d'environnement".
OrangeDog
11

On peut également utiliser un test ApplicationContextInitializer pour initialiser une propriété système:

public class TestApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>
{
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext)
    {
        System.setProperty("myproperty", "value");
    }
}

puis configurez-le sur la classe de test en plus des emplacements des fichiers de configuration de contexte Spring:

@ContextConfiguration(initializers = TestApplicationContextInitializer.class, locations = "classpath:whereever/context.xml", ...)
@RunWith(SpringJUnit4ClassRunner.class)
public class SomeTest
{
...
}

De cette façon, la duplication de code peut être évitée si une certaine propriété système doit être définie pour tous les tests unitaires.

anre
la source
Cela fonctionne également parfaitement avec Spring Boot 2.x et Junit 5.x (en utilisant @SpringBootTestou l'une des annotations de découpage de test )
Wim Deblauwe
11

Toutes les réponses ici ne parlent actuellement que des propriétés du système qui sont différentes des variables d'environnement qui sont plus complexes à définir, esp. pour les tests. Heureusement, la classe ci-dessous peut être utilisée pour cela et la documentation de la classe a de bons exemples

EnvironmentVariables.html

Un exemple rapide tiré de la documentation, modifié pour fonctionner avec @SpringBootTest

@SpringBootTest
public class EnvironmentVariablesTest {
   @ClassRule
   public final EnvironmentVariables environmentVariables = new EnvironmentVariables().set("name", "value");

   @Test
   public void test() {
     assertEquals("value", System.getenv("name"));
   }
 }
Harish Gokavarapu
la source
Les règles EnvironmentVariables font partie d'une bibliothèque tierce, utilisent la réflexion hacky pour modifier les valeurs mises en cache de l'environnement dans la mémoire JVM et ne modifient même pas les variables d'environnement réelles. Je ne voudrais donc pas l'utiliser ou recommander à quiconque de le faire.
Christian
4

Si vous voulez que vos variables soient valides pour tous les tests, vous pouvez avoir un application.propertiesfichier dans votre répertoire de ressources de test (par défaut:) src/test/resourcesqui ressemblera à ceci:

MYPROPERTY=foo

Celui-ci sera ensuite chargé et utilisé à moins que vous n'ayez des définitions via @TestPropertySourceou une méthode similaire - l'ordre exact dans lequel les propriétés sont chargées peut être trouvé dans le chapitre 24 de la documentation de Spring . Configuration externalisée .

blalasaadri
la source
2

Vous pouvez définir les propriétés système comme arguments de machine virtuelle.

Si votre projet est un projet maven, vous pouvez exécuter la commande suivante lors de l'exécution de la classe de test:

mvn test -Dapp.url="https://stackoverflow.com"

Classe de test:

public class AppTest  {
@Test
public void testUrl() {
    System.out.println(System.getProperty("app.url"));
    }
}

Si vous souhaitez exécuter une classe ou une méthode de test individuelle dans eclipse, alors:

1) Allez dans Exécuter -> Exécuter la configuration

2) Sur le côté gauche, sélectionnez votre classe de test dans la section Junit.

3) procédez comme suit:

entrez la description de l'image ici

Joby Wilson Mathews
la source
2

Pour les tests unitaires, la variable système n'est pas encore instanciée lorsque je fais "mvn clean install" car aucun serveur n'exécute l'application. Donc, pour définir les propriétés système, je dois le faire dans pom.xml. Ainsi:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.21.0</version>
    <configuration>
        <systemPropertyVariables>
            <propertyName>propertyValue</propertyName>
            <MY_ENV_VAR>newValue</MY_ENV_VAR>
            <ENV_TARGET>olqa</ENV_TARGET>
            <buildDirectory>${project.build.directory}</buildDirectory>
        </systemPropertyVariables>
    </configuration>
</plugin>
Gène
la source