Тестирование ввода-вывода в F # - PullRequest
4 голосов
/ 29 марта 2012

Учитывая, что у меня есть функция IO:

// this can either be IO or some other side effect 
//that makes the function less pure
printf "HI"

Я хочу проверить, что IO был вызван правильно.Обязательным решением для проверки правильности вызова IO было бы обернуть оператор IO в объект, смоделировать объект, передать объект с использованием внедрения зависимостей и убедиться, что правильный метод был вызван с правильными параметрами.Интересно, если вместо использования внедрения зависимостей для проверки F #, лучшим способом будет проверка вывода функции (утверждая, что возвращено правильное значение или функция) и заглушение вызова IO;следовательно, снова делая функцию чистой, устраняя побочный эффект вызова ввода-вывода.

Я рассматриваю обертывание всех операций ввода-вывода в специальный модуль, например, так.

let MyPrint print statement = print statement ; statement

, чтобы я мог заглушить функцию ввода-вывода и утверждать в своих тестах, что правильная операция произошла так:

тестируемый код:

let PrintHi = fun(print) -> MyPrint print "HI"
let DoNothing = fun(print) -> ()

let DoIf conditional = 
     if conditional then PrintHi
     else DoNothing

FsUnit:

[<Test>] member test.
     let printStub value = ()

     ``Test Hi Is Printed When TRUE`` ()=
          let testedFunc = DoIf true
          testedFunc(printStub) |> should equal PrintHi(printStub)

Это хороший способ проверить побочные эффекты ввода-вывода?Есть ли способ лучше?Пожалуйста, имейте в виду, что моя цель - протестировать любой ввод-вывод, а не просто оператор печати. ​​

Ответы [ 3 ]

7 голосов
/ 30 марта 2012

Вообще говоря, вы хотите отделить чистый код от нечистого (побочного) кода;и сохраняйте код как можно более чистым.

Я рекомендую прочитать эти статьи об этом, они написаны для других функциональных языков, но код, который они используют, прост, а концепции хорошо объяснены и могут быть легко применены в F #(и многие другие языки по этому вопросу):

3 голосов
/ 29 марта 2012

Полагаю, вы действительно не хотите проверять, работает ли printf, как ожидалось (для вас?) - думаю, вы хотите узнать, существует ли еще какой-то функциональный способ, чем DI, чтобы получить тестируемый результаты.

Ответ двоякий: Во-первых: F # - смешанный язык с большой частью ООП - так что да, я бы сделал ваш стандартный шаблон DI с интерфейсами и все такое. Второе: вместо использования этого шаблона вы всегда можете использовать функции более высокого порядка для передачи функций, которые выполняют, например, IO - в вашем случае что-то вроде

let myFunctionUsingIO (printer : string -> unit) (whateverparamsYouNeed) = ...

и затем проверьте это, передав принтер, который утверждает, что ваши требования, - но в конце концов это то же самое, что иметь интерфейс только с одним (неназванным) методом - так что разница очень мала.

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

3 голосов
/ 29 марта 2012

Вы можете написать функцию-обертку, которая временно перенаправляет стандартный вывод во время вызова функции и возвращает записанные значения вместе с результатом функции:

let testPrintf f arg =
    let oldOut = System.Console.Out
    use out = new System.IO.StringWriter()
    System.Console.SetOut(out)
    let res = f arg
    System.Console.SetOut(oldOut)
    (res, out.GetStringBuilder().ToString())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...