Отказывание поведения "System.Console"

Существует ли стандартный способ создания тестового приложения на консоли С#, программируя против интерфейса, а не System.Console?

Например, используя интерфейс IConsole?

Вы сделали это и какие методы вы использовали?

Вы показывали события, когда ваше приложение должно записывать на стандартный вывод?

Ответ 1

Я думаю, что ваш подход с интерфейсом будет работать, и я не думаю, что буду использовать события. Предполагая, что приложение не принимает пользовательский ввод, отличный от параметров командной строки, я бы, вероятно, использовал бы что-то вроде этого для wrap Console.Write/Console.WriteLine:

public interface IConsoleWriter
{
    void Write(string format, params object[] args);
    void WriteLine(string format, params object[] args);
}

Чтобы проверить, я бы либо создал TestConsoleWriter, который сохранил бы все записи в буфере, который я мог бы утверждать против, или я бы создал макет и проверил, что были вызваны Write или WriteLine с параметрами Я ожидал. Если ваше приложение будет обрабатывать тонну записи на консоль (скажем, +100 Мбайт или около того вывода), то использование макета, вероятно, будет предпочтительным по соображениям производительности, но в остальном я бы сказал, выберите какой бы метод вы ни выбрали легче работать с.

Однако этот подход имеет несколько ограничений. Если вы используете какие-либо сборки, которые невозможно изменить, и они пишут на консоль, вы не увидите этот вывод, поскольку вы не можете заставить эти классы использовать IConsoleWriter. Другая проблема заключается в том, что методы Write и WriteLine имеют 18 или около того перегрузок, поэтому вы можете обернуть много методов. Чтобы обойти эти ограничения, вы можете просто использовать метод Console.SetOut для перенаправления вывода консоли на свой собственный TextWriter во время тестирования.

Лично я думаю, что возьму подход SetOut. Это будет только одна строка, которую вы должны добавить в начале ваших модульных тестов (или, возможно, с помощью метода SetUp), и вы можете просто утверждать, что написано в TextWriter.

Ответ 2

Вы хотите изменить поток, который записывает консоль, в модульные тесты. Затем вы можете добавить фиктивный поток или что-то еще. Проверьте этот пост Марк Сееманн на тестировании консоли:

http://blogs.msdn.com/b/ploeh/archive/2006/10/21/consoleunittesting.aspx

Ответ 3

Мне довелось совершить поездку по этой теме вчера: Использование отражения для переопределения таблиц виртуальных методов на С#.

@paulo придумал ответ: LinFu Филиппа Лауреано.

Пример использования Блог разработчика Philip Laureano: перехват Console.WriteLine поможет вам перехватить вызовы на консоль. WriteLine, и (в этом случае) выполнить некоторые дополнительные операции... AOP для .NET... Очень шум, IMHO!

LinFu может быть просто билетом, поскольку он не имеет присутствия внутри вещи, перехваченной, поэтому вы можете "перехватить и изменить" поведение вызовов на сборки сторонних поставщиков "в вашем контекст", но без какой-либо возможности повлиять на ACTUAL поведение перехваченных классов за пределами этого контекста... так что LinFu звучит как хорошее место, чтобы начать реализацию "универсальной тестовой платформы для консольных приложений".

Вы МОЖЕТЕ даже иметь возможность использовать одну из существующих инфраструктур модульного тестирования. Я бы искал рамки с открытым исходным кодом. NUnit приходит на ум. Pun предназначенный.

Удачи с ним в любом случае, это интересный проект. Держите нас в курсе, K?

Приветствия. Кит.

Ответ 4

Я рекомендую использовать moles.

В основном потому, что я предпочитаю, чтобы ваш дизайн определял ваши интерфейсы и классы, а не ваше тестирование.

Ответ 5

Этот код будет работать, если вы используете только один поток:

[TestClass]
public class MyTests
{
    private StringBuilder output;
    private StringWriter tempOutputWriter;
    private TextWriter originalOutputWriter;

    [TestInitialize]
    public void InitializeTest()
    {
        this.originalOutputWriter = Console.Out;
        this.tempOutputWriter = new StringWriter();
        Console.SetOut(tempOutputWriter);
        this.output = tempOutputWriter.GetStringBuilder();
    }

    [TestCleanup]
    public void CleanupTest()
    {
        Console.SetOut(originalOutputWriter);
        this.tempOutputWriter.Dispose();
    }

    [TestMethod]
    public void Test1()
    {
        Program.Main(new string[] { "1", "2", "3" });
        string output = this.output.ToString();
        ...
        this.output.Clear();
    }

    [TestMethod]
    public void Test2()
    {
        Program.Main(new string[] { "4", "5", "6" });
        string output = this.output.ToString();
        ...
        this.output.Clear();
    }
}