2009-10-30 11 views
40

मेरे पास जावा कमांड-लाइन प्रोग्राम है। मैं System.in अनुकरण करने में सक्षम होने के लिए जुनीट टेस्ट केस बनाना चाहता हूं। क्योंकि जब मेरा प्रोग्राम चलता है तो यह लूप में आता है और उपयोगकर्ताओं से इनपुट के लिए इंतजार कर रहा है। मैं जुनीट में अनुकरण कैसे करूं?जुनीट: System.in परीक्षण का अनुकरण कैसे करें?

धन्यवाद

उत्तर

49

System.in स्विच करना तकनीकी रूप से संभव है, लेकिन सामान्य रूप से, यह अधिक मजबूत होगा कि इसे सीधे अपने कोड में कॉल न करें, लेकिन संकेत की एक परत जोड़ें ताकि इनपुट स्रोत आपके आवेदन में एक बिंदु से नियंत्रित हो। वास्तव में आप यह कैसे करते हैं कि एक कार्यान्वयन विस्तार है - निर्भरता इंजेक्शन के सुझाव ठीक हैं, लेकिन आपको तीसरे पक्ष के ढांचे को पेश करने की आवश्यकता नहीं है; उदाहरण के लिए, आप कॉलिंग कोड से I/O संदर्भ को पार कर सकते हैं।

System.in कैसे स्विच करने के लिए:

String data = "Hello, World!\r\n"; 
InputStream stdin = System.in; 
try { 
    System.setIn(new ByteArrayInputStream(data.getBytes())); 
    Scanner scanner = new Scanner(System.in); 
    System.out.println(scanner.nextLine()); 
} finally { 
    System.setIn(stdin); 
} 
1

आप एक कस्टम InputStream बना सकते हैं और System वर्ग

class FakeInputStream extends InputStream { 

    public int read() { 
     return -1; 
    } 
} 

और फिर इसका इस्तेमाल करने के लिए देते साथ कर सकता है आपके Scanner

System.in = new FakeInputStream();

से पहले:

InputStream in = System.in; 
... 
Scanner scanner = new Scanner(in); 

के बाद:

InputStream in = new FakeInputStream(); 
... 
Scanner scanner = new Scanner(in); 

हालांकि मुझे लगता है कि आप बेहतर परीक्षण करने के लिए चाहिए कि कैसे अपनी कक्षा डेटा इनपुट स्ट्रीम से पढ़ने के साथ काम करना चाहिए और वास्तव में नहीं है कि यह कैसे वहाँ से पढ़ता है।

+0

एक TDD दृष्टिकोण से, इस डिजाइन कि परीक्षण "ड्राइविंग" या इंगित करने के लिए प्रयास कर रहा है बचा जाता है। हालांकि, ओपी ने टीडीडी निर्दिष्ट नहीं किया है, और एक टेस्ट-बाद परिप्रेक्ष्य से, यह करना बहुत ही उचित बात है - सिस्टम को वैश्विक स्तर पर लाभ उठाएं। – Yishai

+0

आप System.in = xxx नहीं कर सकते क्योंकि System.in अंतिम है। आप System.setIn का उपयोग कर सकते हैं, लेकिन सुनिश्चित करें कि आप आंसू में डिफ़ॉल्ट पर वापस आते हैं। इसके अलावा आपको अपने स्वयं के इनपुटस्ट्रीम को रोल करने की आवश्यकता नहीं है, बाइटएरेइन इनपुटस्ट्रीम अच्छी तरह से काम करेगा। –

+0

ओह हाँ, मैं उलझन में आया। मैंने जो कहने की कोशिश की थी ... अच्छी तरह से मैं अपनी प्रविष्टि संपादित करूँगा :) – OscarRyz

4

dependency injection का उपयोग करने के लिए अपने कोड को दोबारा करने का प्रयास करें। System.in का उपयोग करने वाली विधि को करने के बजाय, विधि को InputStream को तर्क के रूप में स्वीकार करें। फिर अपने जूनिट परीक्षण में, आप System.in के स्थान पर एक परीक्षण InputStream कार्यान्वयन पास करने में सक्षम होंगे।

7

इस तक पहुंचने के कुछ तरीके हैं। परीक्षण के तहत कक्षा चलाने के दौरान इनपुट इनपुट में पास करने का सबसे पूरा तरीका एक नकली इनपुटस्ट्रीम है जो आपकी कक्षा में अनुरूपित डेटा पास करता है। तुम एक निर्भरता इंजेक्शन ढांचे (जैसे गूगल Guice के रूप में) देख सकते हैं अगर आप अपने कोड में यह एक बहुत क्या करने की जरूरत है, लेकिन आसान तरीका है:

public class MyClass { 
    private InputStream systemIn; 

    public MyClass() { 
     this(System.in); 
    } 

    public MyClass(InputStream in) { 
     systemIn = in; 
    } 
} 

परीक्षण के तहत आप निर्माता कि इनपुट लेता है कहेंगे धारा। आप क्लाउड को उस कन्स्ट्रक्टर पैकेज को निजी बनाते हैं और उसी पैकेज में परीक्षण डालते हैं, ताकि अन्य कोड आम तौर पर इसका उपयोग करने पर विचार न करें।

+3

+1। मैं इस पर तुम्हारे साथ हूँ। मैं एक कदम आगे जाऊंगा: यूनिट परीक्षण में 'इनपुटस्ट्रीम' के आस-पास एक उच्च स्तरीय रैपर के रूप में 'इनपुटडेटा', आपको अपनी कक्षा के बारे में और अधिक ध्यान देना चाहिए, और वास्तव में एकीकरण के बारे में नहीं। – OscarRyz

+0

परिवर्तनीय 'systemIn' बनाने के बजाय आप विधि' System.setIn (in) 'का उपयोग कर सकते हैं। तो आप सामान्य रूप से 'System.in' को कॉल कर सकते हैं लेकिन अद्यतन संस्करण के साथ। – LaraChicharo

3

आप System Rules पुस्तकालय की TextFromStandardInputStream नियम का उपयोग करके कमांड लाइन इंटरफेस के लिए एक स्पष्ट परीक्षण लिख सकते हैं।

public void MyTest { 
    @Rule 
    public final TextFromStandardInputStream systemInMock 
    = emptyStandardInputStream(); 

    @Test 
    public void readTextFromStandardInputStream() { 
    systemInMock.provideLines("foo"); 
    Scanner scanner = new Scanner(System.in); 
    assertEquals("foo", scanner.nextLine()); 
    } 
} 

पूर्ण प्रकटीकरण: मैं उस पुस्तकालय का लेखक हूं।

+0

मैंने अभी अपनी लाइब्रेरी का उपयोग करने की कोशिश की, जो बहुत अच्छा लग रहा था ... लेकिन फिर मैंने इसे नियम सार्वजनिक मॉकिट्रो नियम नियम = मॉकिटोज़ुनीट.रूले() ;, और ऐसा लग रहा था, जहां तक ​​मैंने कोशिश की थी, कि दोनों नहीं कर सके संयुक्त हो जाओ ... तो मैं इसे इंजेक्शन मोजे के साथ गठबंधन करने में सक्षम नहीं था ... –

+0

यह मामला नहीं होना चाहिए। क्या आप सिस्टम नियम लाइब्रेरी के लिए कोई समस्या बना सकते हैं और अपना असफल उदाहरण प्रदान कर सकते हैं। –

+0

धन्यवाद ... अब मुझे पता है कि यह काम करने के लिए है, मैं इसे देख लूंगा और लागू होने पर एक मुद्दा बनाउंगा। यह 'TextFromStandardInputStream' प्रतीत होता था जिसे मुझे कोई समस्या थी, लेकिन' SystemOutRule' ठीक काम करता था। –

1

BufferedReader.readLine() के साथ समस्या यह है कि यह एक अवरोधक विधि है जो उपयोगकर्ता इनपुट के लिए इंतजार कर रही है।ऐसा लगता है कि आप विशेष रूप से अनुकरण करना नहीं चाहते हैं (यानी आप परीक्षण तेजी से करना चाहते हैं)। लेकिन एक परीक्षण संदर्भ में यह परीक्षण के दौरान उच्च गति पर लगातार null लौटाता है, जो अजीब है।

शुद्धिकरण के लिए आप पैकेज-प्राइवेट के नीचे getInputLine बना सकते हैं और इसे मजाक कर सकते हैं: आसान-peezy।

String getInputLine() throws Exception { 
    return br.readLine(); 
} 

... आपको यह सुनिश्चित करना होगा कि आपके पास ऐप के साथ उपयोगकर्ता इंटरैक्शन का एक लूप (आमतौर पर) रोकना है। आपको इस तथ्य का सामना करना पड़ेगा कि आपकी "इनपुट लाइन" हमेशा एक जैसी होगी जब तक कि आपने किसी भी तरह से अपने नकली doReturn को परिवर्तित नहीं किया: उपयोगकर्ता इनपुट की शायद ही सामान्य है।

एक गैर शुद्धतावादी जो खुद के लिए जीवन को आसान बनाने के (और पठनीय परीक्षण उत्पादन) के लिए आपको अपने ऐप्लिकेशन कोड में नीचे यह सब सामान डाल सकता है चाहता है के लिए:

private Deque<String> inputLinesDeque; 

void setInputLines(List<String> inputLines) { 
    inputLinesDeque = new ArrayDeque<String>(inputLines); 
} 

private String getInputLine() throws Exception { 
    if (inputLinesDeque == null) { 
     // ... i.e. normal case, during app run: this is then a blocking method 
     return br.readLine(); 
    } 
    String nextLine = null; 
    try { 
     nextLine = inputLinesDeque.pop(); 
    } catch (NoSuchElementException e) { 
     // when the Deque runs dry the line returned is a "poison pill", 
     // signalling to the caller method that the input is finished 
     return "q"; 
    } 

    return nextLine; 
} 

... अपने परीक्षण में आप हो सकता है तो इस तरह जाएं:

consoleHandler.setInputLines(Arrays.asList(new String[]{ "first input line", "second input line" })); 

इस "कंसोलहैंडलर" वर्ग में विधि को ट्रिगर करने से पहले इनपुट लाइनों की आवश्यकता है।

0

शायद यह (नहीं परीक्षण किया) की तरह:

InputStream save_in=System.in;final PipedOutputStream in = new PipedOutputStream(); System.setIn(new PipedInputStream(in)); 

in.write("text".getBytes("utf-8")); 

System.setIn(save_in); 

अधिक भागों:

//PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out)); 

InputStream save_in=System.in;final PipedOutputStream in = new PipedOutputStream(); System.setIn(new PipedInputStream(in)); 

//start something that reads stdin probably in a new thread 
// Thread thread=new Thread(new Runnable() { 
//  @Override 
//  public void run() { 
//   CoursesApiApp.main(new String[]{});     
//  } 
// }); 
// thread.start(); 


//maybe wait or read the output 
// for(int limit=0; limit<60 && not_ready ; limit++) 
// { 
//  try { 
//   Thread.sleep(100); 
//  } catch (InterruptedException e) { 
//   e.printStackTrace(); 
//  } 
// } 


in.write("text".getBytes("utf-8")); 

System.setIn(save_in); 

//System.setOut(save_out); 
संबंधित मुद्दे