问题
I need to write tests(using google testing framework) for small study program that was written not by me. (it's just small console game which can get modes from command line or just get it in runtime) There is a problem: I can't change the souce code but there is in almost all methods used cout and cin. and my question is "how to answer on requests (cin) of programm while testing (something like get data for cin from string )?".
回答1:
Assuming you can control main()
(or some other function called before the functions to be tested) you can change where std::cin
reads from and where std::cout
writes to:
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);
}
(likewise for std::cout
)
This example saves the original stream buffer of std::cin
so it can be replaced before leaving main()
. It then sets up std::cin
to read from a string stream. It can be any other stream buffer as well.
回答2:
My understanding is you need to perform the following:
- Launch / start target executable (the game).
- Send test data to target executable.
- Obtain output from target executable.
- Compare output with expected results.
The standard C++ language has no standard facilities for communicating with other programs. You will need help from the operating system (which you didn't specify).
Using only C++ or without OS specific calls, I suggest:
- Writing test input to a file.
- Run the executable, piping the test input file as input and piping the output to a results file.
- Read and analyze the result file.
Otherwise, search your OS API to find out how to write to the I/O redirection drivers.
回答3:
I know you said you can't modify the code, but I'll answer this as if you can. The real world typically allows (small) modifications to accommodate testing.
One way is to wrap your calls that require external inputs (DB, user input, sockets, etc...) in function calls that are virtual so you can mock them out. (Example below). But first, a book recommendation on testing. Working Effectively with Legacy Code is a great book for testing techniques that aren't just limited to legacy code.
class Foo {
public:
bool DoesSomething()
{
string usersInput;
cin >> usersInput;
if (usersInput == "foo") { return true; }
else { return false; }
}
};
Would turn into:
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());
}
回答4:
You can improve testability of your classes by not using cin
and cout
directly. Instead use istream&
and ostream&
to pass in the input source and output sink as parameters. This is a case of dependency injection. If you do that, you can pass in a std::stringstream
instead of cin
, so that you can provide specified input and get at the output from your test framework.
That said, you can achieve a similar effect by turning cin and cout into stringstream
s (at least temporarily). To do this, set up a std::stringbuf (or "borrow" one from a std::stringstream
) and use cin.rdbuf(my_stringbuf_ptr)
to change the streambuf
used by cin
. You may want to revert this change in test teardown. To do that you can use code like:
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();
来源:https://stackoverflow.com/questions/14550187/how-to-put-data-in-cin-from-string