如何使用JUnit测试带有Scanner的方法?

4

我有一个读取文件并使用scanner接收用户输入的类,如果scanner等于文件中某行的一部分,则会显示同一行中的字符串。

如何创建Junit测试方法?

以下是我想要测试方法的一些代码:

Scanner Input = new Scanner(System.in);
    String name = Input.nextLine();

    BufferedReader br;
   try{
       br = new BufferedReader(new FileReader(new File(filename)));
       String nextLine;
       while ((nextLine = br.readLine()) != null)
       {
           if (nextLine.startsWith("||"))
           {
                int f1 = nextLine.indexOf("*");
                int f2 = nextLine.indexOf("_");
                fName = nextLine.substring(f1+1, f2);
                   if (name.equals(fname))
                   {
                        String[] s1 = nextLine.split("_");
                        String sName = s1[1];
                        System.out.println(sName);
                   }
           }
       }

我的数据文件看起来像这样:
||
*Jack_Davis
*Sophia_Harrolds

我已经尝试在我的测试方法中使用这段代码。
@Test
public void testgetSurname() {
    System.out.println("get surname");
    String filename = "";
    String expResult = "";
    String result = fileReader.getSurname(filename);
    assertEquals(expResult, result);

    filename = "datafiles/names.txt";
    String data = "Jack";
    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);
      expResult = "Davis";
    }
    String result = fileReader.getSurname(filename);
    assertEquals(expResult, result);                
}
2个回答

5

例如,试试这个:

你可以通过自动模拟控制台来提升它的功能(见下文)。

@Test
public void test_scan() throws Exception
{
Myclass myobject=new myobject(); // with args

myobject.load(filename); // you must definie the filename

String result=myobject.scaninput_and_compare(); // you must use scan in, and compare

if (!result.equals(what_I_am_expecting) throw new Exception("EXCEPTION scaninput_and_compare"); 

// If you arrive here, it's OK
}

如果你想自动化控制台输入,请使用以下方式:

参考:JUnit: How to simulate System.in testing?

String data = "What_I_could_put_in_console";
InputStream stdin = System.in;
System.setIn(new ByteArrayInputStream(data.getBytes()));
Scanner scanner = new Scanner(System.in);
System.setIn(stdin);

注意不要在内部捕获异常,以便使用“好的”System.in来完成。这对于单个测试是可以的,但对于多个测试,您应该进行验证。

使用您的代码:

public String scaninput_and_compare(String filename)
{
Scanner Input = new Scanner(System.in);
String name = Input.nextLine();

BufferedReader br;
 try{
   br = new BufferedReader(new FileReader(new File(filename)));
   String nextLine;
   while ((nextLine = br.readLine()) != null)
   {
       if (nextLine.startsWith("||"))
       {
            int f1 = nextLine.indexOf("*");
            int f2 = nextLine.indexOf("_");
            fName = nextLine.substring(f1+1, f2);
               if (name.equals(fname))
               {
                    String[] s1 = nextLine.split("_");
                    String sName = s1[1];
                    return sName;
               }
       }
   }
// NO GOOD
return "lose";
}


@Test
public void test_scan() throws Exception
{
Myclass myobject=new myobject(); // with args

String filename="good_filename";

// MOCK System.in
String data = "Jack";
InputStream stdin = System.in;
System.setIn(new ByteArrayInputStream(data.getBytes()));

String result=myobject.scaninput_and_compare(filename); // you must use scan in, and compare

// RESTABLISH System.in
Scanner scanner = new Scanner(System.in);
System.setIn(stdin);

if (!result.equals("Davis") throw new Exception("EXCEPTION scaninput_and_compare"); 

// If you arrive here, it's OK
}

更好的设计和更简单的测试:将您的System.in扫描器与文件解析分开。只需使用(filename,fname)函数即可直接进行测试:

assertEquals(myobject.scaninput_and_compare(filename,"Jack"), "Davis");

1
以这种方式并行运行测试可能会非常困惑-无法确定如果在您的测试用例执行期间重置了System.in会发生什么。 - Jan
嗨,感谢您的回复。我正在尝试使用您自动化控制台输入的方式,但是我收到了一个错误消息,显示“未找到行”,我想这可能是因为在我的方法中我有scanner.nextLine();? - MarcusFenix1
@MarcusFenix1,我看不到你的代码,所以无法确认!可能。 - guillaume girod-vitouchkina
已经更新了操作代码。抱歉一开始没有这样做。 - MarcusFenix1
@MarcusFenix1 我已经把所有东西都整合在一起了,你应该捕获System.in的问题,并查看是否正常... - guillaume girod-vitouchkina
非常感谢您的帮助。 - MarcusFenix1

3

一个方法是:

步骤1

重构您的代码,使得Scanner是传递到您的方法中的参数之一。

步骤2

对于您的测试,请使用构造函数Scanner(File file)Scanner(String source)来输入“用户会输入什么”,而在真实世界中(从您的main()),您将创建Scanner(System.in)

或者

重新设计您的代码以提供protected Scanner getScanner() { },然后使用模拟框架(我喜欢Mockito)模拟该方法并返回预处理的字符串Scanner(见步骤2)

所请求的示例(重构)

/**
* Reads the next line from the Scanner
*/
protected String getNextLine(Scanner scanner) {
  //You know how to do that.
}

/** 
*  Check if file contains that name and return matching line.
*  Throws NameNotFoundException (you'd need to create) if not found.
*
*  If it were my code, i'd refactor even more and pass in List<String> 
*  here with the lines from the file
*/
public String matchSurname(String name, File dataFile) throws NameNotFoundException {
   //Iterate over file... 
      if(isMatchingSurname(name, line)) {
        return line;
      }
   // end iteration
   //Still here? Throw Exception!
   throw new NameNotFoundException();
}

/**
* Checks if given Name matches the read line
*/
public boolean isMatchingSurname(String name, String lineFromFile) {
  //All the checks
}

现在,您已经将问题拆分为了一些小块。单元测试现在只适用于个体方法 - 因此,一个用于测试从扫描仪读取行的方法,一个用于测试匹配逻辑,一个用于正确的文件迭代。


你好,能否提供一个示例,说明我该如何处理我的代码? - MarcusFenix1
同意重构。重构使得getSurname()接受一个字符串并且只运行实际的字符串解析代码是否有些过度了? - PunDefeated
@MarcusFenix1 你解决了吗?当你的问题得到解决时,你应该选择最佳答案并“接受”它 - 这样其他人就会知道,你的问题已经得到解决。 - Jan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接