Насмешливое поведение System.Console - PullRequest
16 голосов
/ 02 мая 2011

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

Например, с помощью интерфейса IConsole?

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

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

Ответы [ 5 ]

15 голосов
/ 02 мая 2011

Я думаю, что ваш подход с интерфейсом будет работать, и я не думаю, что использовал бы события.Предполагая, что приложение не принимает пользовательский ввод, кроме параметров командной строки, я бы, вероятно, использовал что-то вроде этого, чтобы обернуть 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 МБ или около того вывода), тогда использование mock, вероятно, будет предпочтительнее по соображениям производительности, но в противном случае я бы сказал, выберите любой метод, который, по вашему мнению, будетс ним проще работать.

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

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

9 голосов
/ 02 мая 2011

Вы хотите изменить поток, в который консоль пишет в ваших модульных тестах. Тогда вы можете положить в ложный поток или что-то еще. Проверьте этот пост от Mark Seemann на тестировании консоли:

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

4 голосов
/ 02 мая 2011

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

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

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

LinFu может быть просто билетом, поскольку он не присутствует в перехваченной вещи, поэтому вы можете «перехватывать и изменять» поведение вызовов для сборок сторонних поставщиков »в пределах ваш контекст", но без какой-либо возможности повлиять на АКТУАЛЬНОЕ поведение перехваченных классов вне этого контекста ... так что LinFu звучит как чертовски хорошее место для начала реализации" общего тестирования - Фреймворк для консольных приложений ".

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

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

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

2 голосов
/ 02 мая 2011

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

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

0 голосов
/ 16 декабря 2018

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

[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();
    }
}
...