亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何使用 PowerMock 測試調用另一個靜態方法的靜態方法?

如何使用 PowerMock 測試調用另一個靜態方法的靜態方法?

千巷貓影 2022-07-14 09:42:36
PowerMock 是一個很棒的工具,我最近開始使用它來測試一些靜態方法。不幸的是,我無法重寫任何東西(除了測試),并且需要 PowerMock 能夠嚴格按原樣測試此代碼。這是我的 PowerMock 測試:import java.io.*;import org.junit.*;import org.junit.runner.RunWith;import static org.junit.Assert.assertEquals;import org.mockito.runners.MockitoJUnitRunner;import org.powermock.core.classloader.annotations.PrepareForTest; @RunWith(MockitoJUnitRunner.class)@PrepareForTest({Solution.class})public class SolutionTest {    // stream to record the output (System.out)    private ByteArrayOutputStream testOutput;    @Before    public void setUpOutputStream() {        testOutput = new ByteArrayOutputStream();        System.setOut(new PrintStream(testOutput));    }    // input feed to Scanner (System.in)    private void setInput(String input) {        System.setIn(new ByteArrayInputStream(input.getBytes()));    }    @Test    public void test1() {        // set System.in        setInput("foo");        final String expected = "foobar";        final String actual = testOutput.toString();        // run the program (empty arguments array)        Solution.main(new String[0]);        assertEquals(expected, actual);    }    @Test    public void test2() {        setInput("new");        Solution.main(new String[0]);        final String expected = "newbar";        final String actual = testOutput.toString();        assertEquals(expected, actual);    }}PowerMock 使我可以在以下場景中對靜態方法連續運行(并通過)兩個測試:import java.util.Scanner;public class Solution {    public static void main(String[] args) {        Scanner scanner = new Scanner(System.in);        String input = scanner.nextLine();        scanner.close();        System.out.print(input + "bar");    }}在 PowerMock 之前,我一直被異常所困擾(由于必須測試靜態方法)java.lang.IllegalStateException: Scanner closed但是,在這種調用第二個靜態方法(scanner 也是靜態成員)的替代方案中,該問題再次出現。在這里,test1 會通過,但 test2 甚至無法運行,因為 java.lang.IllegalStateException: Scanner closed我需要兩個測試在后一種情況下都通過,就像在前一種情況下一樣。
查看完整描述

2 回答

?
繁華開滿天機

TA貢獻1816條經驗 獲得超4個贊

我遇到了類似的問題。我試圖為 HackerRank 挑戰創建一個本地測試環境。我的目標是能夠在提供的半成品Solution課程中完成我的解決方案,并針對從他們的網站下載的測試用例對其進行測試,而無需針對每個挑戰修改樣板代碼。


換句話說,我有一個Solution類,其中包含我無法(閱讀:不想)觸摸的代碼,其中包括由scannerfrom讀取的輸入System.in:


    private static final Scanner scanner = new Scanner(System.in);

我試圖確保在創建System.in該掃描儀實例之前將其設置為所需的值final static,但正如我們從定義中看到的那樣,修改該掃描儀以針對不同的測試用例對其進行定制絕非易事。


另一個棘手的問題是,在Solution類中,輸出被設置為寫入文件,該文件的位置是從環境變量中獲得的,使用 System.getenv("OUTPUT_PATH"). 這會產生一個問題,因為測試可以并行運行并嘗試將結果寫入該環境變量指定的同一文件中。


長話短說,我最終做的是模擬System.classusing PowerMock,為每個測試用例創建一個嵌套類并@PrepareForTest為每個測試用例類添加,這最終對我有用。下面是我的DoAllTest課程代碼,其中包含所有“特定于挑戰”的信息,在這種情況下,它是“炸彈人游戲”挑戰。有兩個測試用例00和25這個挑戰。令人驚訝的是,以下代碼SolutionWrap中包含Solution實例的對象實際上由兩個測試共享,但PowerMock負責模擬System.class并且它們像在單獨的“容器”中一樣運行。


package practice.thebombermangame;

import common.SolutionTest;

import common.SolutionTestable;

import java.io.IOException;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.powermock.core.classloader.annotations.PrepareForTest;

import org.powermock.modules.junit4.PowerMockRunner;

import org.junit.experimental.runners.Enclosed;


@RunWith(Enclosed.class)

public class DoAllTest {

    class SolutionWrap implements SolutionTestable {

        public void runMain(String[] args) {

            try {

                Solution s = new Solution();

                s.main(args);

            } catch (IOException e) {

                System.err.println(e.getMessage());

            }

        };

    };


    static SolutionWrap solutionWrap = new DoAllTest().new SolutionWrap();


    @RunWith(PowerMockRunner.class)

    @PrepareForTest({Solution.class, SolutionTest.class, Test1.class})

    public static class Test1 {

        @Test

        public void test1() {

            String testIDString = "00";

            String inputFileName = "src/practice/thebombermangame/input/input" + testIDString + ".txt";

            String outputFileName = "out_path/output" + testIDString  + ".txt";

            String correctFileName = "src/practice/thebombermangame/output/output" + testIDString + ".txt";

            SolutionTest solutionTest = new SolutionTest(inputFileName, outputFileName, correctFileName);

            solutionTest.doTest(solutionWrap);

        }

    };


    @RunWith(PowerMockRunner.class)

    @PrepareForTest({Solution.class, SolutionTest.class, Test2.class})

    public static class Test2 {

        @Test

        public void test2() {

            String testIDString = "25";

            String inputFileName = "src/practice/thebombermangame/input/input" + testIDString + ".txt";

            String outputFileName = "out_path/output" + testIDString  + ".txt";

            String correctFileName = "src/practice/thebombermangame/output/output" + testIDString + ".txt";

            SolutionTest solutionTest = new SolutionTest(inputFileName, outputFileName, correctFileName);

            solutionTest.doTest(solutionWrap);

        }

    };

}



該類SolutionTest由所有挑戰共享,System.in并且在其中修改環境變量,如下所示:


package common;

import java.io.FileInputStream;

import java.io.IOException;

import org.powermock.api.mockito.PowerMockito;

import org.mockito.Mockito;


public class SolutionTest {

    static String inputFileName;

    String outputFileName;

    String correctFileName;


    public SolutionTest(String inputFileName_, String outputFileName_, String correctFileName_) {

        inputFileName = inputFileName_;

        outputFileName = outputFileName_;

        correctFileName = correctFileName_;

        setSystemIn();

    }


    final static void setSystemIn() {

        try {

            System.out.println("Setting System.in to " + inputFileName);

            System.setIn(new FileInputStream(inputFileName));

        } catch(IOException e) {

            System.err.println(e.getMessage());

        }

    }


    public void doTest(SolutionTestable solutionTestable) {

        PowerMockito.mockStatic(System.class);

        PowerMockito.when(System.getenv(Mockito.eq("OUTPUT_PATH"))).thenReturn(outputFileName);

        SampleTest sampleTest = new SampleTest();

        sampleTest.testMain(solutionTestable, outputFileName, correctFileName);

    }

};

如您所見,在創建對象并設置為傳遞給構造函數setSystemIn()的對象時調用。通過模擬,可以將對象設置為所需的值。SolutionTestSystem.ininputFileNameSystem.classscanner


查看完整回答
反對 回復 2022-07-14
?
慕森卡

TA貢獻1806條經驗 獲得超8個贊

我嘗試了將模擬構造函數PowerMock 的功能與模擬類(與模擬接口相反)Mockito 的功能相結合的東西,但沒有成功:我試圖解決的問題是Scanner實例創建發生在setInput調用之前,所以我嘗試過


private static Scanner scannerMock;


    static {

        try {

            scannerMock = Mockito.mock(Scanner.class);

            PowerMockito.whenNew(Scanner.class).withAnyArguments().thenReturn(scannerMock);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }


    private void setInput(String input) throws Exception {

        PowerMockito.when(scannerMock.nextLine()).thenReturn(input);

    }

這可能適用于其他類,但不適用于Scanner類,因為它是final. 我認為當你不能Solution稍微改變課程時,你的問題沒有解決方案:過去它對我有用,就像這里提出的(免責聲明:由我) ,但如果沒有自由改變它顯然是行不通Solution的的代碼。


可能您可以使用反射來訪問private static final字段Scanner,將其設置為Scanner您之前創建的實例并且您可以控制,如該問題的已接受答案中所述:可能這不是編寫測試的更干凈的方式,但我認為它可以工作并解決您的問題。


我希望這可以幫助您找到一個可接受且可行的解決方案...


查看完整回答
反對 回復 2022-07-14
  • 2 回答
  • 0 關注
  • 389 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號