如何对依赖于Console的switch语句进行unit testing

我是unit testing的新手,我正在使用VS 2010unit testing框架。

我有一个函数可以从用户那里获取一个整数,然后根据用户输入执行不同的函数。 我已经阅读了很多unit testing,但是我没有找到任何能告诉我如何测试switch语句的每个分支的东西。 到目前为止我得到了什么:

[TestMethod] public void RunBankApplication_Case1() { using (var sw = new StringWriter()) { using (var sr = new StringReader("1")) { Console.SetOut(sw); Console.SetIn(sr); BankManager newB = new BankManager(); newB.RunBankApplication(); var result = sw.ToString(); string expected = "Enter Account Number: "; Assert.IsTrue(result.Contains(expected)); } } } 

当调用案例1下的函数时,首先发生的是字符串“输入帐号:”被写入控制台。 但是,这根本不起作用。 我没有正确地将输入传递给控制台吗? 谢谢您的帮助!

编辑:我的RunBankApplication()函数:

 do { DisplayMenu(); option = GetMenuOption(); switch (option) { case 1: if (!CreateAccount()) { Console.WriteLine("WARNING: Could not create account!"); } break; case 2: if (!DeleteAccount()) { Console.WriteLine("WARNING: Could not delete account!"); } break; case 3: if (!UpdateAccount()) { Console.WriteLine("WARNING: Could not update account!"); } break; case 4: DisplayAccount(); break; case 5: status = false; break; default: Console.WriteLine("ERROR: Invalid choice!"); break; } } while (status); 

我想你的RunBankApplication看起来类似于:

 public void RunBankApplication() { var input = Console.ReadLine(); switch (input) { case "1": Console.WriteLine("Enter Account Number: "); break; case "2": Console.WriteLine("Hello World!"); break; default: break; } } 

要了解您在Console上的固定依赖项,使您的方法不可测试,您应该在构造函数中注入此依赖项。

您需要一个定义依赖关系的接口:

 public interface IConsoleService { string ReadLine(); void WriteLine(string message); } 

您为它创建了一个默认实现:

 public class ConsoleService : IConsoleService { public string ReadLine() { return Console.ReadLine(); } public void WriteLine(string message) { Console.WriteLine(message); } } 

然后,在BankManager类中注入此实现,并在类中使用它:

 public class BankManager { private IConsoleService _consoleService; public BankManager(IConsoleService consoleService) { _consoleService = consoleService; } public void RunBankApplication() { var input = _consoleService.ReadLine(); switch (input) { case "1": _consoleService.WriteLine("Enter Account Number: "); break; case "2": _consoleService.WriteLine("Hello World!"); break; default: break; } } } 

现在,您可以在测试中模拟此依赖项。 Moq是这样一个模拟库的不错选择:

 [TestClass] public class UnitTest1 { [TestMethod] public void GetMessage_Input1_ReturnEnterAccountNumberMessage() { var consoleService = new Mock(); consoleService.Setup(c => c.ReadLine()).Returns("1"); var bankManager = new BankManager(consoleService.Object); bankManager.RunBankApplication(); consoleService.Verify(c => c.WriteLine("Enter Account Number: "), Times.Once()); } [TestMethod] public void GetMessage_Input2_ReturnHelloWorldMessage() { var consoleService = new Mock(); consoleService.Setup(c => c.ReadLine()).Returns("2"); var bankManager = new BankManager(consoleService.Object); bankManager.RunBankApplication(); consoleService.Verify(c => c.WriteLine("Hello World!"), Times.Once()); } } 

当然,这对于这样一个简单的例子来说太过分了,但这种方法在大型项目中非常有用。 然后,您可以使用IoC容器在应用程序中自动注入依赖项。

不应该有用户输入自动测试。 您的测试需要提供参数,如果您依赖外部资源(WCF服务,数据库,文件系统,用户输入等等),那么您需要模拟它们。 这就是Moq等测试框架为您所做的事情。

如果要测试switch语句的每个案例,则需要对每个案例进行测试。

顺便说一句,您无法使用控制台,因为您的unit testing可能在类库中而不是控制台应用程序中。

这不是正确的方法。 您不应该在unit testing中与控制台通信。 只需提取与输入参数一起使用的函数并测试此函数。

像这样:

在YourExtractedClass中:

  public string GetMessage(string input) { var result = string.Empty; switch (input) { case "1": result = "Enter Account Number: "; break; case "2": result = "Hello World!"; break; default: break; } return result; } 

….

在YourExtractedClass的Test类中

  [Test] public void GetMessage_Input1_ReturnEnterAccountNumberMessage() { var result = GetMessage("1"); var expected = "Enter Account Number: "; Assert.That(result == expected); } [Test] public void GetMessage_Input2_ReturnHelloWorldMessage() { var result = GetMessage("1"); var expected = "Hello World!"; Assert.That(result == expected); } 

还有一件事:最好将字符串(“输入帐号”等)移动到一个地方(例如,某些常量类)。 不要重复自己!

阅读关于unit testing的好书:

unit testing的艺术:在.Net中的例子

使用NUnit进行C#语用unit testing,第2版

xUnit测试模式:重构测试代码