Я столкнулся с проблемой, подобной этой.Я пытался создать локальную среду тестирования для задач HackerRank.Моя цель состоит в том, чтобы иметь возможность завершить свое решение в предоставленном классе с неполной готовностью Solution
и проверить его на соответствие тестовым примерам, загруженным с их веб-сайта, без необходимости изменения шаблонных кодов для каждой задачи.
В другихслова, у меня есть класс Solution
с кодами, которые я не могу (читай: не хочу) касаться, который включает в себя ввод, прочитанный scanner
из System.in
:
private static final Scanner scanner = new Scanner(System.in);
Я пытался убедиться, что System.in
установлено на желаемое значение до того, как будет создан экземпляр сканера final static
, но, как мы видим по определению, этот сканер отнюдь не легко модифицировать, чтобы настроить его для другого тестаслучаи.
И еще один хитрый бит, в классе Solution
, вывод настроен на запись в файл, местоположение которого получено из переменной среды, используя System.getenv("OUTPUT_PATH")
.Это создает проблему, потому что тесты могут выполняться параллельно и пытаться записать результаты в один и тот же файл, как указано в этой переменной среды.
Короче говоря, я закончил тем, что высмеял System.class
, используя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()
вызывается при создании объекта SolutionTest
и устанавливает System.in
в inputFileName
, который передается в конструктор.При использовании System.class
для объекта scanner
можно установить желаемое значение.