1

我是单元测试的新手,我正在使用 VS 2010 单元测试框架。

我有一个从用户那里获取整数的函数,然后根据用户输入执行不同的函数。我已经阅读了很多关于单元测试的内容,但是我没有找到任何可以告诉我如何测试 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);
4

3 回答 3

4

我想你的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<IConsoleService>();
        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<IConsoleService>();
        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 容器在应用程序中自动注入您的依赖项。

于 2012-11-15T20:52:51.943 回答
2

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

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

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

于 2012-11-15T20:12:29.667 回答
1

这不是正确的方法。您不应该在单元测试中与 Console 进行通信。只需提取与输入参数一起使用的函数并测试此函数。

像这样:

在 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]
    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);
    }

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

阅读有关单元测试的好书:

单元测试的艺术:.Net 中的示例

使用 NUnit 的 C# 中的实用单元测试,第 2 版

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

于 2012-11-15T20:12:34.673 回答