"Привет мир" - путь TDD?
Ну, я думал об этом некоторое время, с тех пор как меня познакомили с TDD.
Какой был бы лучший способ создать приложение "Hello World"? который будет печатать "Hello World" на консоли - с помощью Test Driven Development.
Как бы выглядели мои тесты? и Вокруг каких классов?
Запрос: Нет ссылок, связанных с википедией, с тем, что такое TDD, я знаком с TDD. Просто любопытно, как это можно решить.
Ответы
Ответ 1
Вам нужно скрыть консоль за интерфейсом. (Это может считаться полезным в любом случае)
Записать тест
[TestMethod]
public void HelloWorld_WritesHelloWorldToConsole()
{
// Arrange
IConsole consoleMock = MockRepository.CreateMock<IConsole>();
// primitive injection of the console
Program.Console = consoleMock;
// Act
Program.HelloWorld();
// Assert
consoleMock.AssertWasCalled(x => x.WriteLine("Hello World"));
}
Записать программу
public static class Program
{
public static IConsole Console { get; set; }
// method that does the "logic"
public static void HelloWorld()
{
Console.WriteLine("Hello World");
}
// setup real environment
public static void Main()
{
Console = new RealConsoleImplementation();
HelloWorld();
}
}
Рефакторинг к чему-то более полезному; -)
Ответ 2
Ну... Я не видел TDD-версию приветствия. Но, чтобы увидеть аналогичную простую проблему, к которой обратились TDD и управляемость, вы можете взглянуть на Enterprise FizzBuzz (code). По крайней мере, это позволит вам увидеть уровень чрезмерной инженерии, который вы могли бы достичь в мире приветствий.
Ответ 3
Presenter-View? (модель не кажется строго необходимой)
View будет классом, который передает вывод на консоль (простые однострочные методы)
Presenter - это интерфейс, который вызывает view.ShowText( "Hello World" ), вы можете проверить это, предоставив представление заглушки.
Для производительности, однако, я просто напишу проклятую программу:)
Достаточно одного теста (в псевдокоде):
IView view = Stub<IView>();
Expect( view.ShowText("Hello World") );
Presenter p = new Presenter( view );
p.Show();
Assert.IsTrue( view.MethodsCalled );
Ответ 4
Псевдо-код:
- Создайте макет того, что принимает поток.
- Вызывать helloworld на этот макет через какую-то инъекцию зависимостей (как аргумент конструктора).
- Убедитесь, что строка "Hello World" была передана в ваш макет.
В производственном коде вы используете приглашение вместо макета.
Общее правило:
- Определите критерии успеха в том, как компонент взаимодействует с другим материалом, а не только тем, как он взаимодействует с вами. TDD фокусируется на внешнем поведении.
- Настройка среды (mocks) для обработки цепочки событий.
- Запустите его.
- Проверьте.
Ответ 5
Предполагая, что вы знаете модульное тестирование, и, учитывая, что вы понимаете "красный зеленый процесс рефакторинга" (поскольку вы сказали, что знакомы с TDD), я быстро объясню типичный процесс мышления tdd.
Ваша жизнь в TDD будет намного легче, если вы подумаете об определенной единице проблемы, и все другие связанные вещи должны рассматриваться в терминах зависимостей. здесь приведен пример
сценарий: -
Я хочу, чтобы моя программа отображала мир приветствия на консоли.
Процесс мышления tdd: -
"Я думаю, что моя программа запустится, а затем вызовите консольную программу, передающую мое сообщение, а затем я ожидаю, что моя консольная программа отобразит ее на экране"
", поэтому мне нужно проверить, что, когда я запускаю свою программу, он должен вызывать консольную программу"
"теперь, каковы зависимости: hmm Я знаю, что консольная программа - одна из них. Мне не нужно беспокоиться о том, как консоль получит сообщение на экран (вызов устройства io, печать и все такое ) Мне просто нужно знать, что моя программа успешно вызвала консольную программу. Мне нужно верить, что консольная программа работает, а если нет, то на данный момент я не отвечаю за тестирование и убеждаюсь, что это работает. хочу проверить, что моя программа при запуске вызывает консольную программу.
", но я даже не знаю точно, какую консольную программу вызывать. Я знаю System.console.Writeline(конкретная реализация), но тогда это может измениться в будущем из-за изменения требований, так что я делаю?"
"Ну, я буду зависеть от интерфейса (или абстракции), а не от конкретной реализации, тогда я могу создать поддельную консоль, реализующую интерфейс, который я могу проверить против"
public interface Iconsole
{
void WriteToConsole(string msg);
}
public class FakeConsole : Iconsole
{
public bool IsCalled = false;
public void WriteToConsole(string msg)
{
IsCalled = true;
}
}
Я добавил член IsCalled, чье "состояние" изменится, если вызывается когда-либо консольная программа
OK Я знаю, что это звучит как долгий процесс размышлений, но он окупается. Tdd заставляет вас думать перед кодированием, что лучше, чем кодирование, прежде чем думать
В конце дня вы можете придумать что-то вроде следующего способа вызова вашей программы:
var console = new FakeConsole();
console.IsCalled = false;
my_program program = new my_program(console);
program.greet();
Я передал консоль в my_program, так что my_program будет использовать консоль для записи нашего сообщения на экран.
и моя my_program может выглядеть так:
public class my_program
{
Iconsole _consol;
public my_program(Iconsole consol)
{
if (consol != null)
_consol = consol;
}
public void greet()
{
_consol.WriteToConsole("Hello world");
}
}
окончательный unit test будет: -
[TestMethod]
public void myProgramShouldDisplayHelloWorldToTheConsole()
{
//arrange
var console = new FakeConsole();
console.IsCalled = false;
my_program program = new my_program(console);
//act
program.greet();
//assert
Assert.AreEqual(true, console.IsCalled, " console was not called to display the greeting");
}
Ответ 6
В java вы можете захватить ( "перенаправить" ) поток System.out и прочитать его содержимое. Я уверен, что то же самое можно было бы сделать на С#. Это всего лишь несколько строк кода в java, поэтому я уверен, что это не будет намного больше в С#
Ответ 7
Очень интересный вопрос. Я не являюсь огромным пользователем TDD, но я выскажу некоторые мысли.
Я предполагаю, что приложение, которое вы хотите проверить, следующее:
public static void Main()
{
Console.WriteLine("Hello World");
}
Теперь, поскольку я не могу придумать какой-либо хороший способ проверить это непосредственно, я бы разложил задачу записи в интерфейс.
public interface IOutputWriter
{
void WriteLine(string line);
}
public class ConsoleWriter : IOutputWriter
{
public void WriteLine(string line)
{
Console.WriteLine(line);
}
}
И сломайте приложение так:
public static void Main()
{
IOutputWriter consoleOut = new ConsoleWriter();
WriteHelloWorldToOutput(consoleOut);
}
public static void WriteHelloWorldToOutput(IOutputWriter output)
{
output.WriteLine("Hello World");
}
Теперь у вас есть точка инъекции к методу, который позволяет вам использовать насмешливую структуру по вашему выбору, чтобы утверждать, что метод WriteLine вызывается с параметром "Hello World".
Проблемы, которые я оставил нерешенными (и меня будет интересовать ввод):
-
Как проверить класс ConsoleWriter, я думаю, вы все еще нуждаются в рамках тестирования UI для достижения этой цели, и если у вас что то вся проблема в тоо так или иначе...
-
Тестирование основного метода.
-
Почему я чувствую, что я что-то добился, изменив одну строку непроверенного кода на семь строк кода, только один из которых фактически протестирован (хотя я предполагаю, что покрытие увеличилось)
Ответ 8
Мне действительно нужно возражать против вопроса! Все методологии имеют свое место, и TDD хорош во многих местах. Но пользовательские интерфейсы - это первое место, которое я действительно отступаю от TDD. Это, по моему скромному мнению, является одним из лучших оправданий шаблона проектирования MVC: протестируйте червь из своих моделей и контроллера программно; визуально осмотрите свое мнение. То, о чем вы говорите, это жесткое кодирование данных "Hello World" и тестирование, которое оно выводит на консоль. Чтобы выполнить этот тест на одном и том же исходном языке, вы в значительной степени должны заглушить объект консоли, который является единственным объектом, который вообще что-то делает.
В качестве альтернативы вы можете script выполнить тест в bash:
echo `java HelloWorldProgram`|grep -c "^Hello World$"
Немного сложно добавить в набор тестов JUnit, но что-то говорит мне, что никогда не было плана...
Ответ 9
Я согласен с Дэвидом Бергером; отделить интерфейс и проверить модель. Похоже, что "модель" в этом случае представляет собой простой класс, который возвращает "Hello, world!". Тест будет выглядеть так (на Java):
Greeter greeter = new Greeter();
assertEquals("Hello World!", greeter.greet());
Я создал запись решения стиля Hello World
TDD в http://ziroby.wordpress.com/2010/04/18/tdd_hello_world/.
Ответ 10
Я думаю, что-то вроде этого:
using NUnit.Framework;
using System.Diagnostics;
[TestFixture]
public class MyTestClass {
[Test]
public void SayHello() {
string greet = "Hello World!";
Debug.WriteLine(greet);
Assert.AreEqual("Hello World!", greet);
}
}