200 votes

Comment tester du code dépendant de variables d'environnement avec JUnit ?

J'ai un morceau de code Java qui utilise une variable d'environnement et le comportement du code dépend de la valeur de cette variable. J'aimerais tester ce code avec différentes valeurs de la variable d'environnement. Comment puis-je faire cela dans JUnit ?

J'ai vu quelques façons de définir des variables d'environnement en Java en général, mais je suis plus intéressé par l'aspect des tests unitaires, surtout si l'on considère que les tests ne devraient pas interférer les uns avec les autres.

12voto

Mangusta Points 413

Je ne pense pas que cela ait déjà été mentionné, mais vous pouvez également utiliser Powermockito :

Compte tenu de ce qui précède :

package com.foo.service.impl;

public class FooServiceImpl {

    public void doSomeFooStuff() {
        System.getenv("FOO_VAR_1");
        System.getenv("FOO_VAR_2");
        System.getenv("FOO_VAR_3");

        // Do the other Foo stuff
    }
}

Vous pouvez procéder comme suit :

package com.foo.service.impl;

import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;

import org.junit.Beforea;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(FooServiceImpl.class)
public class FooServiceImpTest {

    @InjectMocks
    private FooServiceImpl service;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        mockStatic(System.class);  // Powermock can mock static and private methods

        when(System.getenv("FOO_VAR_1")).thenReturn("test-foo-var-1");
        when(System.getenv("FOO_VAR_2")).thenReturn("test-foo-var-2");
        when(System.getenv("FOO_VAR_3")).thenReturn("test-foo-var-3");
    }

    @Test
    public void testSomeFooStuff() {        
        // Test
        service.doSomeFooStuff();

        verifyStatic();
        System.getenv("FOO_VAR_1");
        verifyStatic();
        System.getenv("FOO_VAR_2");
        verifyStatic();
        System.getenv("FOO_VAR_3");
    }
}

10voto

sudocode Points 9483

Cette réponse à la question Comment définir des variables d'environnement à partir de Java ? fournit un moyen de modifier la carte (non modifiable) dans System.getenv(). Ainsi, bien que cela ne change pas VRAIMENT la valeur de la variable d'environnement du système d'exploitation, cela peut être utilisé pour les tests unitaires, car cela modifie ce que le système d'exploitation doit faire. System.getenv reviendra.

10voto

Andrea Colleoni Points 3144

Découpler le code Java de la variable d'environnement en fournissant un lecteur de variable plus abstrait que vous réalisez avec un EnvironmentVariableReader que votre code à tester lit.

Ensuite, dans votre test, vous pouvez donner une implémentation différente du lecteur de variables qui fournit vos valeurs de test.

L'injection de dépendance peut y contribuer.

7voto

George Z. Points 6025

Même si je pense que cette réponse est le meilleur pour les projets Maven, il peut également être réalisé via reflect (testé dans Java 8 ) :

public class TestClass {
    private static final Map<String, String> DEFAULTS = new HashMap<>(System.getenv());
    private static Map<String, String> envMap;

    @Test
    public void aTest() {
        assertEquals("6", System.getenv("NUMBER_OF_PROCESSORS"));
        System.getenv().put("NUMBER_OF_PROCESSORS", "155");
        assertEquals("155", System.getenv("NUMBER_OF_PROCESSORS"));
    }

    @Test
    public void anotherTest() {
        assertEquals("6", System.getenv("NUMBER_OF_PROCESSORS"));
        System.getenv().put("NUMBER_OF_PROCESSORS", "77");
        assertEquals("77", System.getenv("NUMBER_OF_PROCESSORS"));
    }

    /*
     * Restore default variables for each test
     */
    @BeforeEach
    public void initEnvMap() {
        envMap.clear();
        envMap.putAll(DEFAULTS);
    }

    @BeforeAll
    public static void accessFields() throws Exception {
        envMap = new HashMap<>();
        Class<?> clazz = Class.forName("java.lang.ProcessEnvironment");
        Field theCaseInsensitiveEnvironmentField = clazz.getDeclaredField("theCaseInsensitiveEnvironment");
        Field theUnmodifiableEnvironmentField = clazz.getDeclaredField("theUnmodifiableEnvironment");
        removeStaticFinalAndSetValue(theCaseInsensitiveEnvironmentField, envMap);
        removeStaticFinalAndSetValue(theUnmodifiableEnvironmentField, envMap);
    }

    private static void removeStaticFinalAndSetValue(Field field, Object value) throws Exception {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, value);
    }
}

5voto

Muthu Points 51

J'espère que le problème est résolu. Je voulais juste vous faire part de ma solution.

Map<String, String> env = System.getenv();
    new MockUp<System>() {
        @Mock           
        public String getenv(String name) 
        {
            if (name.equalsIgnoreCase( "OUR_OWN_VARIABLE" )) {
                return "true";
            }
            return env.get(name);
        }
    };

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X