Разработка F # и модульное тестирование?

108

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

До сих пор мой процесс с F # заключался в том, чтобы написать некоторые функции, поиграть с ними с помощью интерактивной консоли, пока я не «достаточно» уверен, что они работают, а также настроить и объединить. Это хорошо работает с небольшими задачами, такими как проект Эйлера, но я не могу представить, чтобы таким образом можно было построить что-то большое.

Как люди подходят к модульному тестированию и созданию набора тестов для программы на F #? Есть ли эквивалент TDD? Любые указатели или мысли приветствуются.

Матиас
источник
1
expert-fsharp.com/CodeSamples/Forms/… показывает простой пример использования NUnit с F #.
itowlson 01
см. stackoverflow.com/questions/1468772/…
Маурисио Шеффер
связанные: stackoverflow.com/questions/5667372/… (Unquote - это гораздо больше, чем сноска / комментарий, поскольку он находится в наборе ответов на этой странице)
Рубен Бартелинк
Одна вещь, отсутствующая в этих ответах, - это надлежащий пример Foq, AutoFixture.AutoFoq и AutoFixture.xUnit, связанных с выводом типа F #. См. Trelford.com/blog/post/test5.aspx и trelford.com/blog/post/fstestlang.aspx для дегустатора, и когда-нибудь я напишу здесь правильный ответ
Рубен Бартелинк

Ответы:

77

Разработчики, ориентированные на тестирование, должны чувствовать себя как дома с функциональными языками, такими как F #: небольшие функции, которые дают детерминированно воспроизводимые результаты, идеально подходят для модульных тестов. В языке F # также есть возможности, облегчающие написание тестов. Возьмем, к примеру, объектные выражения . Вы можете очень легко написать подделки для функций, которые принимают в качестве входных данных тип интерфейса.

Во всяком случае, F # - это первоклассный объектно-ориентированный язык, и вы можете использовать те же инструменты и приемы, что и при выполнении TDD на C #. Также существуют некоторые инструменты тестирования, написанные на F # или специально для него:

Мэтью Подвизоцкий написал отличную серию статей о модульном тестировании на функциональных языках. Дядя Боб также написал мысль провоцирующей статьи здесь .

Рэй Вернагус
источник
9
Я также разработал (и активно развиваю) специальную библиотеку модульного тестирования для F # под названием Unquote: code.google.com/p/unquote . Он позволяет вам писать тестовые утверждения в виде простых, статически проверенных логических выражений F # с использованием F # Quotations и автоматически генерировать хорошие сообщения об ошибках теста. Он работает без конфигурации со специальной поддержкой как для xUnit.net, так и для NUnit и обычно поддерживает любую структуру модульного тестирования на основе исключений. Он работает даже в рамках сеансов FSI, обеспечивая плавный переход от интерактивного тестирования к формальным тестовым пакетам.
Стивен Свенсен
Еще есть Pex , хотя его немного сложнее нащупать.
Бенджол 08
1
Дядя Боб линк кажется мертвым
Aage
Обновление 2020: чтобы быть исчерпывающим, Expecto github.com/haf/expecto следует добавить в список (см. Сообщение автора ниже). ИМО, это самый FP-фреймворк для тестирования F #
Матье Франсуа
22

Я использую NUnit, и мне не кажется, что его трудно читать или писать:

open NUnit.Framework

[<TestFixture>]
type myFixture() = class

    [<Test>]
    member self.myTest() =
       //test code

end

Поскольку мой код представляет собой смесь F # и других языков .Net, мне нравится тот факт, что я пишу модульные тесты в основном одинаково и с аналогичным синтаксисом как на F #, так и на C #.

Дэвид Глаубман
источник
4
Прочитав здесь другие отзывы, я попробовал FSUnit, и я думаю, что это здорово. Он хорошо работает с TestDriven.Net (как и NUnit), поощряет гибкий стиль написания самодокументированных тестов и, как утверждает Рэй, «больше подходит для языков F #». Неплохо для 21 строки кода! (И пара рекомендаций по макету / именованию). Два небольших замечания: 1. Предварительно скомпилированная библиотека FSUnit DLL у меня не работала. Сборка из исходного кода (FsUnit.NUnit-0.9.0.fs) устранила проблему. 2. TestDriven.Net не распознает имена TextFixture, которые выглядят `like this`. Названия тестов, использующие форму двойной галочки, распознаются.
Дэвид Глаубман
15

Взгляните на FsCheck , инструмент автоматического тестирования F #, который по сути является портом QuickCheck от Haskell. Он позволяет вам предоставить спецификацию программы в форме свойств, которым должны удовлетворять функции или методы, а FsCheck проверяет, что свойства сохраняются в большом количестве случайно сгенерированных случаев.

Страница FsCheck CodePlex

Страница автора FsCheck

первородный
источник
Да, я думаю, что FsCheck предлагает гораздо больше, чем традиционные фреймворки для модульного тестирования, такие как NUnit и т. Д.
Роберт
11

Как предлагает dglaubman, вы можете использовать NUnit. xUnit.net также поддерживает это и хорошо работает с TestDriven.net . Код похож на тесты NUnit, но без требования обертывать тест в содержащий тип.

#light

// Supply a module name here not a combination of module and namespace, otherwise
// F# cannot resolve individual tests nfrom the UI.
module NBody.DomainModel.FSharp.Tests

open System
open Xunit

open Internal

[<Fact>]
let CreateOctantBoundaryReordersMinMax() =
    let Max = VectorFloat(1.0, 1.0, 1.0)
    let Min = VectorFloat(-1.0, -1.0, -1.0)

    let result = OctantBoundary.create Min Max

    Assert.Equal(Min, result.Min)     
    Assert.Equal(Max, result.Max) 
Аде Миллер
источник
Начиная с 1.9.1, новые перегрузки Xunit, похоже, вызывают хаос в моем F #.
Rick Minerich
@RickMinerich Я испытал то же самое с моим кодом. Я просто добавил явные аннотации типов, чтобы выбрать правильную перегрузку. Тем не менее, это действительно добавить больше шума к коду , к сожалению.
Erik Schierboom 02
11

Я думаю, что это очень интересный вопрос, над которым я сам много думал. Пока что мои мысли - это только мысли, поэтому примите их такими, какие они есть.

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

Одна из основных сильных сторон .NET - это кросс-языковые возможности. Я знаю, что скоро собираюсь писать производственный код F #, но я планирую писать модульные тесты на C #, чтобы облегчить себе путь к тому, что для меня является новым языком. Таким образом, я также могу проверить, что то, что я пишу на F #, будет совместимо с C # (и другими языками .NET).

При таком подходе я понимаю, что есть определенные функции F #, которые я могу использовать только для внутренних целей в моем коде F #, но не раскрывать как часть моего общедоступного API, но я приму это, как и сегодня, что есть определенные вещи. C # позволяет мне выражать (как uint) несовместимые с CLS, поэтому я воздерживаюсь от их использования.

Марк Симанн
источник
2
Как продвигался твой план? Было легко протестировать код f # с кодом c #? Я начал изучать f #, и мой план - написать часть моего проекта на f #, и у меня та же идея: писать модульные тесты на C # и для f #.
Питер Порфи
@ Отметить какие-нибудь обновления? Мне также трудно разобраться в потоке, используя TDD с F #.
Скотт Нимрод
1
@ScottNimrod Довольно много обновлений: четыре моих курса Pluralsight посвящены тестированию или TDD с F #. Вы также найдете множество бесплатных записей конференций в моем профиле Lanyrd . Наконец, есть мой блог .
Марк Зееманн,
3
@ScottNimrod Я не могу рекомендовать тратить время и / или платить деньги, чтобы просмотреть полный набор курсов PS Марка достаточно высоко - он соберет все вместе в вашей голове с максимальной эффективностью. Хотя это может или не может относиться к вашим конкретным потребностям, «Функциональная архитектура на F #» также объединяет множество точек, и ее также следует тщательно рассмотреть.
Рубен Бартелинк,
7

Вы можете взглянуть на FSUnit - хотя я еще не использовал его, возможно, стоит попробовать. Конечно, лучше, чем использовать, например, (собственный) NUnit в F #.

ShdNx
источник
1
ShdNx, почему вы рекомендуете против NUnit? Книга Дона Сайма по F # показывает NUnit для тестирования, и это очень похоже на использование NUnit в C #. FSUnit DSL выглядит круто, но для людей, уже знакомых с NUnit (Матиас «использовал TDD в течение многих лет»), по вашему опыту, использование NUnit с F # более проблематично, чем с C # или VB?
itowlson 01
Я второй комментарий и вопрос Итоулсона. Определенно, NUnit в F # выглядит довольно странно, но, кроме того, знаете ли вы о конкретных проблемах, по которым стоит использовать что-то еще?
Матиас
1
Я бы сказал, что «выглядеть довольно странно» - это обычно веская причина найти что-то получше. Странный взгляд означает, что трудно читать, а трудно читать - это ошибки. (Я предполагаю, что «выглядеть странно» полностью отличается от «выглядеть новым и / или незнакомым» - незнакомое станет знакомым, странное останется странным.)
Джеймс Мур
1
Если честно (как я уже упоминал в своем ответе), я еще не использовал FSUnit, но читал, что использовать NUnit в F # очень болезненно. Извините, если это не так.
ShdNx 02
4
Чтобы было ясно, у вас по-прежнему будут члены TestFixtures и Test, если вы используете FsUnit. Чего у вас не будет, так это стандартных вызовов Assert.X. FsUnit просто предоставляет вам оболочку для этой части NUnit, которая делает его более удобным для языка F #.
Рэй Вернагус 02
2

Несмотря на то, что я немного опоздал на вечеринку, я хотел бы поприветствовать Матиаса на F # (лучше поздно, чем никогда;)) и сообщить, что вам может понравиться моя библиотека модульного тестирования Expecto

Expecto имеет некоторые функции, которые могут вам понравиться:

  • Синтаксис F # во всем, тесты как значения; напишите простой F # для генерации тестов
  • Используйте встроенный модуль Expect или внешнюю библиотеку, например Unquote, для утверждений
  • Параллельные тесты по умолчанию
  • Протестируйте свой код Hopac или асинхронный код; Expecto работает асинхронно
  • Подключаемое ведение журнала и метрики через Logary Facade; легко писать адаптеры для систем сборки или использовать механизм синхронизации для создания информационной панели InfluxDB + Grafana времени выполнения ваших тестов
  • Встроенная поддержка BenchmarkDotNet
  • Встроенная поддержка FsCheck; упрощает создание тестов со сгенерированными / случайными данными или построение инвариантных моделей пространства состояний вашего объекта / актора

-

open Expecto

let tests =
  test "A simple test" {
    let subject = "Hello World"
    Expect.equal subject "Hello World" "The strings should equal"
  }

[<EntryPoint>]
let main args =
  runTestsWithArgs defaultConfig args tests

https://github.com/haf/expecto/

Хенрик
источник