2013-07-26 27 views
4

我在这个小问题上摸不着头脑。如何对无效输入进行单元测试“console.readLine”?

我有此线和3对其它类似的那些方法中的

try 
{ 
    int PhoneIMEINumber = int.Parse(Console.ReadLine()); 
} 

{ 
catch(Exception) 
{ 
    return null; 
} 

如果用户输入“ABCD”输入,这会引发异常,我可以抓住它,并显示一个错误消息。

但是我该如何做一个单元测试呢?我无法模拟单元测试的控制台输入,我想从单元测试中检查是否返回null。

谢谢

+5

您可能不应该单元测试控制台,而是测试业务规则。 – Romoku

+8

单元测试的一大优点是它可以让你重新评估你的设计。你现在应该这样做并重构你的代码。目前,ReadLine与业务/验证代码紧密耦合。 –

+0

@HenkHolterman汉克,我有一个场景,我想测试一个方法,分析我的计算机上的文件,这是由用户通过'Console.ReadLine()'指定的。这应如何处理? – alex

回答

5

就像发表的两条评论。我会考虑重构你的代码看起来更像

string input = Console.ReadLine(); 

try 
{ 
    int PhoneIMEINumber = parse_input(input); 
} 
catch(Exception) 
{ 
    return null; 
} 

,然后你就会有一个功能

public int parse_input(string input) 
{ 
    return int.Parse(input); 
} 

,那么你会写的parse_input功能的单元测试。虽然示例代码看起来相当平凡,但很难证明围绕int.Parse()的包装函数编写单元测试是正确的,但我假设将来您的解析可能会变得更加复杂。

+0

超级...这很简单...正是我需要+1 –

+0

+1,因为您实际上是唯一一个将业务逻辑与基础架构分开的人。当验证IMEI号码时,所有其他答案只是试图抽象出控制台,因为它与真正无关。 – jgauffin

+0

@jgauffin那么OP特意问了如何单元测试'控制台',尽管我更喜欢这种方法。 – Romoku

6

您可以设置Console.In使用SetIn给定的文本阅读器:

var sr = new StringReader("Invalid int"); 
Console.SetIn(sr); 

int? parsed = MethodUnderTest(); 
Assert.IsNull(parsed); 
+0

这也是我首先想到的。我很困惑为什么这个答案有这么几个upvotes?这不行吗? – mafu

+0

我正在为使用控制台的hello-world应用编写测试用例,这正是我想要的。例如:输入您的姓名验证是否返回“您好”。谢谢。 –

+0

使用Console.SetIn是最实际的答案。您可以模拟传递的TextReader并验证ReadLine调用计数。 –

7

如果你想单元测试Console,那么你可能需要创建一个包装接口。

public interface IConsole 
{ 
    string ReadLine(); 
} 

public class ConsoleWrapper : IConsole 
{ 
    public string ReadLine() 
    { 
     return Console.ReadLine(); 
    } 
} 

这样您就可以创建一个存根/假来测试您的业务规则。

public class TestableConsole : IConsole 
{ 
    private readonly string _output; 

    public TestableConsole(string output) 
    { 
     _output = output; 
    } 

    public string ReadLine() 
    { 
     return _output; 
    } 
} 

测试内部:

public class TestClass 
{ 
    private readonly IConsole _console; 

    public TestClass(IConsole console) 
    { 
     _console = console; 
    } 

    public void RunBusinessRules() 
    { 
     int value; 
     if(!int.TryParse(_console.ReadLine(), out value) 
     { 
      throw new ArgumentException("User input was not valid"); 
     } 
    } 
} 

[Test] 
public void TestGettingInput() 
{ 
    var console = new TestableConsole("abc"); 

    var classObject = new TestClass(console); 

    Assert.Throws<ArgumentException>(() => classObject.RunBusinessRules()); 
} 

我会试图避免单元测试Console,并承担它的依赖性去。

+1

++经典依赖注入 –

+0

谢谢...非常详细的答案,超!! +1。 Al W的解决方案更适合我的解决方案。 –

+0

是的,我不会推荐使用这个解决方案,除非你有特定的需求。 – Romoku

0

要单元测试代码,那段代码需要可测试。

要单元测试一个方法,我们做的是传入一些已知的输入,并检查它是否返回预期的结果,或者如果我们有正确的副作用。

现在,如果你直接在你的方法中输入内容,它不是很好的测试。它有两个部分,读取输入和处理它。如果测试中的断言失败,则不知道哪一步失败。单元测试应该旨在测试一个单元或一个代码的单一责任。

要使其可测试,请读取main方法中的输入并将其传递给另一个处理方法(也是面向对象编程的常见概念),然后使用已知参数测试第二个方法。

相关问题