模拟用户输入并检查输出的简单方法
最近,我的一些學(xué)生向我詢問了赫爾辛基大學(xué)MOOC提供的單元測(cè)試的機(jī)制,我檢查了它們的實(shí)現(xiàn),并認(rèn)為這對(duì)于初學(xué)者了解實(shí)際發(fā)生的情況是有幫助的,因此在此發(fā)表了這篇小文章。
我們將以“機(jī)場(chǎng)”項(xiàng)目為例,這是OOP2第一周的最后一項(xiàng)任務(wù)。
我們僅關(guān)注測(cè)試,因此我將跳過有關(guān)如何解決它的事情。 在本練習(xí)中,我們將每次手動(dòng)執(zhí)行main方法,重復(fù)輸入飛機(jī)編號(hào),容量,有時(shí)我們認(rèn)為我們的代碼可以工作,然后運(yùn)行本地測(cè)試,以便可以提交給服務(wù)器進(jìn)行在線判斷和評(píng)分。
我一直使用這個(gè)小項(xiàng)目作為借助單元測(cè)試保護(hù)的重構(gòu)示例。 當(dāng)我又重復(fù)又痛苦地輸入飛機(jī)ID,航班號(hào),機(jī)場(chǎng)代碼和操作代碼時(shí),我問我的學(xué)生:“這很痛苦嗎?”。
顯然,他們所有人都回答了。 然后我問,“即使無(wú)聊又痛苦,您會(huì)一次又一次地進(jìn)行這種測(cè)試嗎?”
安靜。
從我過去的經(jīng)驗(yàn)中,我知道跳過這些無(wú)聊的測(cè)試很容易,并且我們可以安慰自己,“這些代碼非常簡(jiǎn)單,我不會(huì)犯錯(cuò),它將起作用并且會(huì)起作用,不用擔(dān)心。”
由于做出這樣的選擇,我會(huì)留下痛苦的回憶,因?yàn)檫^去我犯了太多簡(jiǎn)單而愚蠢的錯(cuò)誤,所以無(wú)論看起來(lái)多么簡(jiǎn)單,我仍然會(huì)進(jìn)行測(cè)試-即使是手動(dòng)測(cè)試,也無(wú)聊而痛苦。
我添加此內(nèi)容是因?yàn)閱卧獪y(cè)試無(wú)法完全替代手動(dòng)測(cè)試,盡管它將使手動(dòng)測(cè)試更加容易和有效。
對(duì)于Airport項(xiàng)目,如果不需要每次都重復(fù)輸入,并且可以捕獲程序的輸出,則與預(yù)期相比,我們將更快地獲得反饋。
String operation = scanner.nextLine(); ... System.out.println("Blahblahblah...");例如,我們確切地知道是否首先輸入x ,然后它將進(jìn)入飛行服務(wù)部分并打印菜單選項(xiàng);如果我們第二次輸入x ,則程序?qū)⒔Y(jié)束循環(huán)并退出,結(jié)果,我們將僅獲取機(jī)場(chǎng)面板和飛行服務(wù)的說明輸出。
因此,讓我們轉(zhuǎn)到一個(gè)測(cè)試用例,看看實(shí)際會(huì)發(fā)生什么。
@Test public void printsMenusAndExits() throws Throwable {String syote = "x\nx\n";MockInOut io = new MockInOut(syote);suorita(f(syote));String[] menuRivit = {"Airport panel","[1] Add airplane","[2] Add flight","[x] Exit","Flight service","[1] Print planes","[2] Print flights","[3] Print plane info","[x] Quit"};String output = io.getOutput();String op = output;for (String menuRivi : menuRivit) {int ind = op.indexOf(menuRivi);assertRight(menuRivi, syote, output, ind > -1);op = op.substring(ind + 1);} }上面是第二個(gè)測(cè)試用例,它涵蓋了我們所說的最簡(jiǎn)單的情況,僅輸入兩個(gè)x 。
當(dāng)我們查看測(cè)試代碼時(shí),它分為三部分:
- 準(zhǔn)備輸入
- 執(zhí)行Main.main(args)方法
- 檢查輸出以查看它是否依次包含所有預(yù)期行
您知道scanner.nextLine()或scanner.nextInt()的正常行為。 該程序?qū)炱鸩⒌却脩糨斎?#xff0c;以便執(zhí)行下一行代碼。 但是,為什么它在這里沒有任何等待就可以順利運(yùn)行?
在開始這一部分之前,我想簡(jiǎn)要解釋一下該方法的執(zhí)行,它使用Java反射以一種不直接但可以進(jìn)行更多檢查的方式來(lái)調(diào)用該方法,例如,第一個(gè)測(cè)試用例要求Main為公共類,但您可能會(huì)發(fā)現(xiàn)要通過手動(dòng)測(cè)試,可以將Main訪問級(jí)別設(shè)置為package。
@Test public void classIsPublic() {assertTrue("Class " + klassName + " should be public, so it must be defined as\n" +"public class " + klassName + " {...\n}", klass.isPublic()); }這里klass.isPublic()正在檢查是否根據(jù)需要設(shè)置訪問級(jí)別。
好。 看來(lái)MockInOut類使魔術(shù)發(fā)生了,我們可以檢查代碼以在MockInOut找到想法。 您可以在GitHub上訪問源代碼。
public MockInOut(String input) {orig = System.out;irig = System.in;os = new ByteArrayOutputStream();try {System.setOut(new PrintStream(os, false, charset.name()));} catch (UnsupportedEncodingException ex) {throw new RuntimeException(ex);}is = new ByteArrayInputStream(input.getBytes());System.setIn(is); }您可能已經(jīng)輸入System.out數(shù)千次,但是您是否意識(shí)到可以像上面一樣默默地更改out ? 這同時(shí)設(shè)置out與in系統(tǒng)的,這樣我們就可以完全執(zhí)行后得到的輸出,我們也不需要手工輸入這個(gè)時(shí)候,因?yàn)樵诼暶鱏canner scanner = new Scanner(System.in); ,則參數(shù)System.in會(huì)以無(wú)提示方式更改,因此scanner.nextLine()將獲得準(zhǔn)備好的輸入而不會(huì)掛起。
同樣,輸出將不會(huì)在控制臺(tái)中打印,而是會(huì)累積到ByteArrayOutputStream ,此后可以訪問。
您可能想知道,如果我們真的要恢復(fù)System.in和System.out的正常行為,該怎么辦?
/*** Restores System.in and System.out*/ public void close() {os = null;is = null;System.setOut(orig);System.setIn(irig); }基本上,它節(jié)省了原來(lái)in和out ,需要恢復(fù)時(shí),只需再次清除遭入侵的人,并設(shè)置他們回來(lái),那么一切都將照常進(jìn)行。
您可以在下面復(fù)制簡(jiǎn)單的示例代碼以進(jìn)行快速測(cè)試。
import java.io.*; import java.util.*;class HelloWorld {public static void main(String[] args) throws IOException {PrintStream orig = System.out;ByteArrayOutputStream os = new ByteArrayOutputStream();System.setOut(new PrintStream(os, false, "UTF-8"));// Here it won't print but just accumulatefor (int i = 0; i < 100; i++) {System.out.println("Hello World");}System.setOut(orig);// Print 100 lines of "Hello World" here since out was restoredSystem.out.println(os.toString("UTF-8"));InputStream is = System.in;System.setIn(new ByteArrayInputStream("x\nx\n".getBytes()));Scanner scanner = new Scanner(System.in);// Without hang onSystem.out.println(scanner.nextLine());System.out.println(scanner.nextLine());try {// There are only two lines provided, so here will failSystem.out.println(scanner.nextLine());} catch (NoSuchElementException e) {e.printStackTrace();}System.setIn(is);scanner = new Scanner(System.in);// Hang on here since `in` was restoredSystem.out.println(scanner.nextLine());} }實(shí)際上,注入和替換是一種用于分離單元測(cè)試依賴關(guān)系的常用方法,這對(duì)于僅關(guān)注代碼非常有用。 還有更先進(jìn),更復(fù)雜的方法來(lái)做到這一點(diǎn),但在這里,我們只是想說明一個(gè)簡(jiǎn)單的方法是“黑客” in和out ,這樣你可以專注于你的代碼,而不是in與out 。
對(duì)于某些遺留項(xiàng)目,此方法可能對(duì)重構(gòu)至關(guān)重要,因?yàn)樘嗟囊蕾囮P(guān)系使測(cè)試變得非常困難!
翻譯自: https://www.javacodegeeks.com/2019/02/approach-simulate-input-check-output.html
總結(jié)
以上是生活随笔為你收集整理的模拟用户输入并检查输出的简单方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SAR合成孔径雷达距离多普勒(RD)算法
- 下一篇: apache camel_在WildFl