Я хочу создать модульный тест на функцию членства названного класса ScoreBoard
который хранит лучшие пять плееров в игре.
Проблема состоит в том, что метод я создал тест для (SignInScoreBoard
) звонит Console.ReadLine()
таким образом, пользователь может ввести их имя:
public void SignInScoreBoard(int steps)
{
if (topScored.Count < 5)
{
Console.Write(ASK_FOR_NAME_MESSAGE);
string name = Console.ReadLine();
KeyValuePair<string, int> pair = new KeyValuePair<string, int>(name, steps);
topScored.Insert(topScored.Count, pair);
}
else
{
if (steps < topScored[4].Value)
{
topScored.RemoveAt(4);
Console.Write(ASK_FOR_NAME_MESSAGE);
string name = Console.ReadLine();
topScored.Insert(4, new KeyValuePair<string, int>(name, steps));
}
}
}
Существует ли способ вставить как десять пользователей, таким образом, я могу проверить, хранятся ли пять с меньшим количеством перемещений (шаги)?
Вам нужно будет преобразовать строки кода, вызывающие Console.ReadLine, в отдельный объект, чтобы вы могли заменить его своей собственной реализацией в ваших тестах.
В качестве быстрого примера вы можете просто создать такой класс:
public class ConsoleNameRetriever {
public virtual string GetNextName()
{
return Console.ReadLine();
}
}
Затем в своем методе реорганизуйте его, чтобы взамен взять экземпляр этого класса. Однако во время тестирования вы можете переопределить это с помощью тестовой реализации:
public class TestNameRetriever : ConsoleNameRetriever {
// This should give you the idea...
private string[] names = new string[] { "Foo", "Foo2", ... };
private int index = 0;
public override string GetNextName()
{
return names[index++];
}
}
При тестировании замените реализацию тестовой реализацией.
Конечно, я бы лично использовал фреймворк, чтобы упростить эту задачу, и использовать чистый интерфейс вместо этих реализаций, но, надеюсь, вышеизложенного достаточно, чтобы дать вам правильное представление ...
Почему бы не создать новый поток (файл / память) как для стандартного ввода, так и для стандартного вывода, а затем перенаправить ввод / вывод в новые потоки перед вызовом метода? Затем вы можете проверить содержимое потоков после завершения метода.
Вместо того, чтобы абстрагироваться от консоли, я бы предпочел создать компонент для инкапсуляции этой логики, протестировать этот компонент и использовать его в консольном приложении.
public void SignInScoreBoard(int steps, Func<String> nameProvider)
{
...
string name = nameProvider();
...
}
В вашем тестовом примере вы можете назвать это как
SignInScoreBoard(val, () => "TestName");
В вашей обычной реализации назовите его как
SignInScoreBoard(val, Console.ReadLine);
Если вы используете C # 4.0, вы можете сделать Console.ReadLine значением по умолчанию значение, сказав
public void SignInScoreBoard(int steps, Func<String> nameProvider=null)
{
nameProvider = nameProvider ?? Console.ReadLine;
...
Вы можете использовать Moles, чтобы заменить Console.ReadLine
своим собственным методом без необходимости изменять код вообще (разработка и реализация абстрактной консоли с поддержкой инъекции зависимостей - все это совершенно не нужно).
Вам следует рефакторить свой код, чтобы убрать зависимость от консоли из этого кода.
Например, вы можете сделать так:
public interface IConsole
{
void Write(string message);
void WriteLine(string message);
string ReadLine();
}
и затем изменить ваш код следующим образом:
public void SignInScoreBoard(int steps, IConsole console)
{
... just replace all references to Console with console
}
Чтобы запустить его в production, передайте ему экземпляр этого класса:
public class ConsoleWrapper : IConsole
{
public void Write(string message)
{
Console.Write(message);
}
public void WriteLine(string message)
{
Console.WriteLine(message);
}
public string ReadLine()
{
return Console.ReadLine();
}
}
Однако во время тестирования используйте этот:
public class ConsoleWrapper : IConsole
{
public List<String> LinesToRead = new List<String>();
public void Write(string message)
{
}
public void WriteLine(string message)
{
}
public string ReadLine()
{
string result = LinesToRead[0];
LinesToRead.RemoveAt(0);
return result;
}
}
Это облегчит тестирование вашего кода.
Конечно, если вы хотите проверить, что правильный вывод также записывается, вам нужно добавить код в методы write для сбора вывода, чтобы вы могли утверждать об этом в вашем тестовом коде.
Не могу поверить, сколько людей ответили, не посмотрев на вопрос должным образом. Проблема в том, что рассматриваемый метод делает более чем одну вещь, т.е. запрашивает имя и вставляет высший балл. Любая ссылка на консоль может быть удалена из этого метода, а вместо нее должно быть передано имя:
public void SignInScoreBoard(int steps, string nameOfTopScorer)
Для других тестов вы, вероятно, захотите абстрагироваться от чтения вывода консоли, как было предложено в других ответах.