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

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您之前創建的實例并且您可以控制,如該問題的已接受答案中所述:可能這不是編寫測試的更干凈的方式,但我認為它可以工作并解決您的問題。
我希望這可以幫助您找到一個可接受且可行的解決方案...
添加回答
舉報