【问题标题】:How to put data in cin from string如何将字符串中的数据放入cin
【发布时间】:2013-01-11 02:12:42
【问题描述】:

我需要为不是我编写的小型学习程序编写测试(使用谷歌测试框架)。 (它只是一个小型控制台游戏,可以从命令行获取模式或只是在运行时获取它) 有一个问题:我无法更改源代码,但几乎所有方法都使用了 cout 和 cin。我的问题是“如何在测试时回答程序的请求(cin)(例如从字符串获取 cin 的数据)?”。

【问题讨论】:

  • ./yourProg
  • io重定向只能用于命令?
  • 我无法使用可执行文件
  • @greensher:谁说的?是的,它可以。
  • @ybungalobill:我使用谷歌测试框架编写测试。如何在 C++ 中进行重定向?

标签: c++ unit-testing cin


【解决方案1】:

我的理解是您需要执行以下操作:

  1. 启动/启动目标可执行文件(游戏)。
  2. 将测试数据发送到目标可执行文件。
  3. 从目标可执行文件获取输出。
  4. 将输出与预期结果进行比较。

标准 C++ 语言没有与其他程序通信的标准工具。您将需要操作系统的帮助(您没有指定)。

使用仅 C++不使用操作系统特定调用,我建议:

  1. 将测试输入写入文件。
  2. 运行可执行文件,将测试输入文件作为输入和管道传输 输出到结果文件。
  3. 读取并分析结果文件。

否则,请搜索您的操作系统 API 以了解如何写入 I/O 重定向驱动程序。

【讨论】:

    【解决方案2】:

    我知道你说过你不能修改代码,但我会尽可能地回答这个问题。现实世界通常允许(小的)修改以适应测试。

    一种方法是将需要外部输入(数据库、用户输入、套接字等)的调用封装在虚拟函数调用中,以便您可以模拟它们。 (示例如下)。但首先,关于测试的书籍推荐。 Working Effectively with Legacy Code 是一本很棒的书,用于测试不仅限于遗留代码的技术。

    class Foo {
    public:
       bool DoesSomething() 
       {
          string usersInput;
          cin >> usersInput;
          if (usersInput == "foo") { return true; }
          else { return false; }
       }
    };
    

    会变成:

    class Foo
    {
    public:
       bool DoesSomething() {
          string usersInput = getUserInput();
          if (usersInput == "foo") { return true; }
          else { return false; }
       }
    
    protected:
       virtual std::string getUserInput() {
          string usersInput;
          cin >> usersInput;
          return usersInput;
       }
    
    };
    
    class MockFoo : public Foo {
    public:
       void setUserInput(std::string input) { m_input = input }
       std::string getUserInput() {
          return m_input;
       }
    };
    
    TEST(TestUsersInput)
    {
       MockFoo foo;
       foo.setUserInput("SomeInput");
       CHECK_EQUAL(false, foo.DoesSomething());
    
       foo.setUserInput("foo");
       CHECK_EQUAL(true, foo.DoesSomething());
    }
    

    【讨论】:

      【解决方案3】:

      假设您可以控制main()(或在要测试的函数之前调用的其他函数),您可以更改std::cin 的读取位置和std::cout 的写入位置:

      int main(int ac, char* av[]) {
          std::streambuf* orig = std::cin.rdbuf();
          std::istringstream input("whatever");
          std::cin.rdbuf(input.rdbuf());
          // tests go here
          std::cin.rdbuf(orig);
      }
      

      (同样适用于std::cout

      此示例保存std::cin 的原始流缓冲区,因此可以在离开main() 之前对其进行替换。然后它设置std::cin 从字符串流中读取。它也可以是任何其他流缓冲区。

      【讨论】:

        【解决方案4】:

        您可以通过不直接使用cincout 来提高类的可测试性。而是使用istream&ostream& 将输入源和输出接收器作为参数传入。这是一个依赖注入的例子。如果这样做,您可以传入std::stringstream 而不是cin,这样您就可以提供指定的输入并从测试框架获取输出。

        也就是说,您可以通过将 cin 和 cout 转换为 stringstreams 来实现类似的效果(至少暂时如此)。为此,请设置一个 std::stringbuf(或从std::stringstream“借用”一个)并使用cin.rdbuf(my_stringbuf_ptr) 更改cin 使用的streambuf。您可能希望在测试拆解中恢复此更改。为此,您可以使用如下代码:

        stringbuf test_input("One line of input with no newline", ios_base::in);
        stringbuf test_output(ios_base::out);
        streambuf * const cin_buf = cin.rdbuf(&test_input);
        streambuf * const cout_buf = cout.rdbuf(&test_output);
        test_func(); // uses cin and cout
        cout.rdbuf(cout_buf);
        cin.rdbuf(cin_buf);
        string test_output_text = test_output.str();
        

        【讨论】:

        • 这是一个很好的一般规则。不仅仅是可测试性会得到改善;代码总体上将更加灵活且易于维护。您应该输出到std::cout 或从std::cin 输入的唯一时间是用于快速测试程序或实验。
        猜你喜欢
        • 2011-04-17
        • 2021-12-15
        • 1970-01-01
        • 2016-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多