JUnit का उपयोग करके पर्यावरण चर पर निर्भर कोड का परीक्षण कैसे करें?


140

मेरे पास जावा कोड का एक टुकड़ा है जो एक पर्यावरण चर का उपयोग करता है और कोड का व्यवहार इस चर के मूल्य पर निर्भर करता है। मैं पर्यावरण चर के विभिन्न मूल्यों के साथ इस कोड का परीक्षण करना चाहूंगा। JUnit में मैं यह कैसे कर सकता हूं?

मैंने सामान्य रूप से जावा में पर्यावरण चर सेट करने के कुछ तरीके देखे हैं , लेकिन मुझे इसके यूनिट परीक्षण पहलू में अधिक दिलचस्पी है, खासकर यह देखते हुए कि परीक्षणों को एक-दूसरे के साथ हस्तक्षेप नहीं करना चाहिए।


चूंकि यह परीक्षण के लिए है, सिस्टम नियम इकाई परीक्षण नियम वर्तमान में सबसे अच्छा जवाब हो सकता है।
आतिफ

3
ज्यूनिट 5 का उपयोग करते समय एक ही प्रश्न के इच्छुक लोगों के लिए: stackoverflow.com/questions/46846503/…
फेलिप मार्टिन्स मेलो

जवाबों:


199

पुस्तकालय प्रणाली नियम पर्यावरण चर स्थापित करने के लिए एक न्याय नियम प्रदान करता है।

import org.junit.contrib.java.lang.system.EnvironmentVariables;

public class EnvironmentVariablesTest {
  @Rule
  public final EnvironmentVariables environmentVariables
    = new EnvironmentVariables();

  @Test
  public void setEnvironmentVariable() {
    environmentVariables.set("name", "value");
    assertEquals("value", System.getenv("name"));
  }
}

डिस्क्लेमर: मैं सिस्टम रूल्स का लेखक हूं।


1
मैं इसे @ClassRule के रूप में उपयोग कर रहा हूं, क्या मुझे उपयोग करने के बाद इसे रीसेट या साफ़ करने की आवश्यकता है, यदि हाँ तो कैसे?
मृत्युंजय

आप की जरूरत नहीं है। कक्षा में सभी परीक्षण निष्पादित होने के बाद मूल पर्यावरण चर स्वचालित रूप से नियम द्वारा रीसेट हो जाते हैं।
स्टीफन बिर्कनर

यह दृष्टिकोण केवल JUnit 4 या उच्चतर संस्करण के लिए काम करता है। JUnit 3 या निचले संस्करण के लिए अनुशंसित नहीं है या यदि आप JUnit 4 और JUnit 3 को मिलाते हैं
RLD

2
import org.junit.contrib.java.lang.system.EnvironmentVariables;आपको com.github.stefanbirkner:system-rulesअपनी परियोजना में निर्भरता को जोड़ना होगा । यह मावेनसेन्ट्रल में उपलब्ध है।
जीन बॉब

2
यहां निर्भरता जोड़ने के निर्देश हैं: stefanbirkner.github.io/system-rules/download.html
Guilherme Garnier

77

सामान्य समाधान एक ऐसा वर्ग तैयार करना है जो इस पर्यावरण चर तक पहुंच का प्रबंधन करता है, जिसे आप अपनी परीक्षा कक्षा में मॉक कर सकते हैं।

public class Environment {
    public String getVariable() {
        return System.getenv(); // or whatever
    }
}

public class ServiceTest {
    private static class MockEnvironment {
        public String getVariable() {
           return "foobar";
        }
    }

    @Test public void testService() {
        service.doSomething(new MockEnvironment());
    }
}

परीक्षण के तहत वर्ग तब पर्यावरण चर का उपयोग करके पर्यावरण चर पाता है, सीधे System.getenv () से नहीं।


1
मुझे पता है कि यह प्रश्न पुराना है, लेकिन मैं कहना चाहता था कि यह सही उत्तर है। स्वीकृत उत्तर सिस्टम पर एक छिपी निर्भरता के साथ खराब डिजाइन को प्रोत्साहित करता है, जबकि यह उत्तर सिस्टम को एक अन्य निर्भरता के रूप में उचित डिजाइन उपचार प्रणाली को प्रोत्साहित करता है जिसे इंजेक्ट किया जाना चाहिए।
एंड्रयू

30

इसी तरह की स्थिति में जहां मुझे टेस्ट केस लिखना पड़ा जो कि पर्यावरण चर पर निर्भर है , मैंने निम्नलिखित कोशिश की:

  1. मैं स्टीफन बिर्कनर द्वारा सुझाए गए सिस्टम रूल्स के लिए गया था । इसका उपयोग सरल था। लेकिन जल्द ही बाद में, मैंने व्यवहार को अनियमित पाया। एक रन में, यह काम करता है, अगले भाग में यह विफल हो जाता है। मैंने जांच की और पाया कि सिस्टम नियम JUnit 4 या उच्चतर संस्करण के साथ अच्छी तरह से काम करते हैं। लेकिन मेरे मामलों में, मैं कुछ जार का उपयोग कर रहा था जो कि JUnit 3 पर निर्भर थे । इसलिए मैंने सिस्टम रूल्स को छोड़ दिया । इस पर अधिक आप यहाँ पा सकते हैं @ नियम एनोटेशन JUnit में TestSuite का उपयोग करते समय काम नहीं करता है
  2. आगे मैंने जावा द्वारा प्रदान की गई प्रोसेस बिल्डर क्लास के माध्यम से पर्यावरण चर बनाने की कोशिश की । यहां जावा कोड के माध्यम से हम एक पर्यावरण चर बना सकते हैं, लेकिन आपको उस प्रक्रिया या कार्यक्रम के नाम को जानना होगा जो मैंने नहीं किया था। इसके अलावा यह मुख्य प्रक्रिया के लिए नहीं, बाल प्रक्रिया के लिए पर्यावरण चर बनाता है।

मैंने उपरोक्त दो दृष्टिकोणों का उपयोग करके एक दिन बर्बाद किया, लेकिन कोई फायदा नहीं हुआ। तब मावेन मेरे बचाव में आया। हम मावेन पोम फ़ाइल के माध्यम से पर्यावरण चर या सिस्टम गुण सेट कर सकते हैं, जो मुझे मावेन आधारित परियोजना के लिए यूनिट परीक्षण करने का सबसे अच्छा तरीका लगता है । नीचे मैंने POM फ़ाइल में प्रविष्टि की है ।

    <build>
      <plugins>
       <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
          <systemPropertyVariables>
              <PropertyName1>PropertyValue1</PropertyName1>                                                          
              <PropertyName2>PropertyValue2</PropertyName2>
          </systemPropertyVariables>
          <environmentVariables>
            <EnvironmentVariable1>EnvironmentVariableValue1</EnvironmentVariable1>
            <EnvironmentVariable2>EnvironmentVariableValue2</EnvironmentVariable2>
          </environmentVariables>
        </configuration>
      </plugin>
    </plugins>
  </build>

इस बदलाव के बाद, मैंने फिर से टेस्ट केस चलाए और अचानक सभी ने उम्मीद के मुताबिक काम किया। पाठक की जानकारी के लिए, मैंने मावेन 3.x में इस दृष्टिकोण का पता लगाया , इसलिए मुझे मावेन 2.x पर कोई विचार नहीं है ।


2
यह समाधान सबसे अच्छा है और इसे स्वीकार किया जाना चाहिए, क्योंकि आपको किसी अतिरिक्त चीज की आवश्यकता नहीं होगी। मावेन अकेले काफी काम आता है। साभार @RLD
Semo

@ सेमो के लिए हालांकि मावेन की आवश्यकता होती है, जो कि एक काम का उपयोग करने की तुलना में बहुत बड़ी आवश्यकता है। इसने जून टेस्ट को पोम तक जोड़ा है, और टेस्ट को हमेशा सामान्य तरीके से सीधे IDE पर चलाने के बजाय, mvan से निष्पादित करने की आवश्यकता होती है।
चिरालो

@ चिरलो, यह इस बात पर निर्भर करता है कि आप क्या चाहते हैं कि आपका कार्यक्रम टिक जाए। मावेन का उपयोग करके, आप एक स्थान पर कॉन्फ़िगर कर सकते हैं और स्वच्छ और संक्षिप्त कोड लिख सकते हैं। यदि आप पुस्तकालय का उपयोग करते हैं, तो आपको कई स्थानों पर कोड लिखना होगा। JUnits चलाने की अपनी बात के बारे में, आप EUse की तरह IDE से JUnits चला सकते हैं, भले ही आप मावेन का उपयोग करें।
RLD

@RLD, इकलौते तरीके से मुझे पता है कि ग्रहण के दौरान इसे एक 'मावेन' रन कॉन्फ़िगरेशन के रूप में चलाया जाएगा, जो कि बहुत अधिक बोझिल है और सामान्य जुनिट दृश्य के बजाय सिर्फ टेक्स्ट आउटपुट है। और मैं नीट और संक्षिप्त कोड के आपके बिंदु का पालन नहीं करता हूं और कई स्थानों पर कोड लिखना चाहता हूं। मेरे लिए, एक जून परीक्षण में उपयोग किए जाने वाले पोम में परीक्षण डेटा का एक साथ होना अधिक अस्पष्ट है। मैं हाल ही में इस स्थिति में था और मैथ्यूफ़रवेल के दृष्टिकोण के बाद समाप्त हो गया, पुस्तकालयों / पोम ट्रिक्स की कोई आवश्यकता नहीं थी और एक ही परीक्षण में सब कुछ एक साथ है।
चिरलो

1
यह पर्यावरण चर को कठोर-कोडित बनाता है, और उन्हें System.getenv के एक मंगलाचरण से अगले में नहीं बदला जा सकता है। सही बात?
इयान स्टीवर्ट

12

मुझे लगता है कि ऐसा करने का सबसे साफ तरीका Mockito.spy () है। यह एक अलग वर्ग को मॉक और पास करने के लिए बनाने की तुलना में थोड़ा अधिक हल्का है।

अपने पर्यावरण चर को दूसरी विधि में ले जाएं:

@VisibleForTesting
String getEnvironmentVariable(String envVar) {
    return System.getenv(envVar);
}

अब आपकी इकाई परीक्षा में ऐसा करते हैं:

@Test
public void test() {
    ClassToTest classToTest = new ClassToTest();
    ClassToTest classToTestSpy = Mockito.spy(classToTest);
    Mockito.when(classToTestSpy.getEnvironmentVariable("key")).thenReturn("value");
    // Now test the method that uses getEnvironmentVariable
    assertEquals("changedvalue", classToTestSpy.methodToTest());
}

12

मुझे नहीं लगता कि इसका उल्लेख अभी तक किया गया है, लेकिन आप पावरमॉकिटो का उपयोग भी कर सकते हैं :

दिया हुआ:

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
    }
}

आप निम्नलिखित कर सकते हैं:

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");
    }
}

8
when(System.getenv("FOO_VAR_1")).thenReturn("test-foo-var-1")org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'.त्रुटि का कारण बनता है
Andremoniy

10

पर्यावरण कोड से जावा कोड को घटाएं और अधिक अमूर्त चर पाठक प्रदान करें जिसे आप पढ़ते समय पढ़े जाने वाले पर्यावरणविद्या से अपने कोड के साथ जोड़ते हैं।

फिर अपने परीक्षण में आप चर पाठक का एक अलग कार्यान्वयन दे सकते हैं जो आपके परीक्षण मूल्यों को प्रदान करता है।

निर्भरता इंजेक्शन इसमें मदद कर सकता है।


9

इस सवाल का जवाब मैं जावा से पर्यावरण चर कैसे सेट करूं? System.getenv () में मैप (unmodifiable) मैप को बदलने का एक तरीका प्रदान करता है। इसलिए जब तक यह वास्तव में OS पर्यावरण चर के मूल्य को नहीं बदलता है, इसका उपयोग इकाई परीक्षण के लिए किया जा सकता है क्योंकि यह परिवर्तन करता है कि System.getenv वापस आ जाएगा।


4

आशा है कि समस्या सुलझ जाएगी। मैंने सिर्फ अपना हल बताने के लिए सोचा।

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);
        }
    };

1
आप यह उल्लेख करना भूल गए कि आप JMockit का उपयोग कर रहे हैं। :) भले ही, यह समाधान JUnit 5 के साथ भी अच्छा काम करता है
रयान जे। मैकडोनो

2

भले ही मुझे लगता है कि यह उत्तर मावेन परियोजनाओं के लिए सबसे अच्छा है, इसे प्रतिबिंबित (साथ ही जावा 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);
    }
}

इसके लिए धन्यवाद! जावा के मेरे संस्करण में ऐसा प्रतीत नहीं होता है theCaseInsensitiveEnvironmentऔर इसके बजाय एक फ़ील्ड है theEnvironment, जैसे निम्नलिखित: `` `envMap = new HashMap <> (); कक्षा <?> Clazz = Class.forName ("java.lang.ProcessEnvironment"); फ़ील्ड theEnvironmentField = clazz.getDeclaredField ("theEnvironment"); फ़ील्ड theUododifiableEnvironmentField = clazz.getDeclaredField ("theUnmodifiableEnvironment"); removeStaticFinalAndSetValue (theEnvironmentField, envMap); removeStaticFinalAndSetValue (theUnmodifiableEnvironmentField, envMap); `` `
इंटेक्स

-2

वैसे आप अपने env के विभिन्न मूल्यों को घोषित करने के लिए सेटअप () विधि का उपयोग कर सकते हैं। स्थिरांक में चर। फिर अलग-अलग परिदृश्य का परीक्षण करने के लिए परीक्षण विधियों में इन स्थिरांक का उपयोग करें।


-2

यदि आप जावा में पर्यावरण चर के बारे में informations प्राप्त करना चाहते हैं, तो आप विधि को कॉल कर सकते हैं System.getenv();:। गुणों के रूप में, यह विधि एक मानचित्र देती है जिसमें चर नाम कुंजी के रूप में होते हैं और चर मान मानचित्र मानों के रूप में होते हैं। यहाँ एक उदाहरण है :

    import java.util.Map;

public class EnvMap {
    public static void main (String[] args) {
        Map<String, String> env = System.getenv();
        for (String envName : env.keySet()) {
            System.out.format("%s=%s%n", envName, env.get(envName));
        }
    }
}

विधि getEnv()एक तर्क भी ले सकती है। उदाहरण के लिए :

String myvalue = System.getEnv("MY_VARIABLE");

परीक्षण के लिए, मैं ऐसा कुछ करूंगा:

public class Environment {
    public static String getVariable(String variable) {
       return  System.getenv(variable);
}

@Test
 public class EnvVariableTest {

     @Test testVariable1(){
         String value = Environment.getVariable("MY_VARIABLE1");
         doSometest(value); 
     }

    @Test testVariable2(){
       String value2 = Environment.getVariable("MY_VARIABLE2");
       doSometest(value); 
     }   
 }

1
मुख्य बिंदु जूनियर टेस्ट से एनवी चर का उपयोग नहीं करना है
तन्मय भट्टाचार्जी

-2

मैं मानचित्र प्राप्त करने के लिए System.getEnv () का उपयोग करता हूं और मैं एक क्षेत्र के रूप में रखता हूं, इसलिए मैं इसका मजाक उड़ा सकता हूं:

public class AAA {

    Map<String, String> environmentVars; 

    public String readEnvironmentVar(String varName) {
        if (environmentVars==null) environmentVars = System.getenv();   
        return environmentVars.get(varName);
    }
}



public class AAATest {

         @Test
         public void test() {
              aaa.environmentVars = new HashMap<String,String>();
              aaa.environmentVars.put("NAME", "value");
              assertEquals("value",aaa.readEnvironmentVar("NAME"));
         }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.