- PVSM.RU - https://www.pvsm.ru -
Вы наверное уже слышали много хорошего о языке F#, и даже наверное успели его опробовать на небольших личных проектах. Но как быть если речь идет о чем-то немного большем чем просто запуск и отладка простого консольного приложения или скрипта? В этой статье я поведаю вам о моем личном опыте работы с тестами в F#. Кому интересно, прошу в подкат.
Для удобства изложения материала я подготовил небольшой проект, исходный код которого доступен здесь [1]. Исходники содержат небольшой модуль и тесты к нему. Вот собственно и сам модуль:
[<AutoOpen>]
module DistanceUnits
open System
[<Measure>] type m
[<Measure>] type cm
[<Measure>] type inch
[<Measure>] type ft
[<Measure>] type h
let mPerCm : float<m/cm> = 0.01<m/cm>
let cmPerInch : float<cm/inch> = 2.54<cm/inch>
let inchPerFeet: float<inch/ft> = 12.0<inch/ft>
let metersToCentimeters (x: float<m>) = x / mPerCm
let centimetersToInches (x: float<cm>) = x / cmPerInch
let inchesToFeets (x:float<inch>) = x / inchPerFeet
let centimetersToMeters: float<cm> -> float<m> = ( * ) mPerCm
let inchesToCentimeters: float<inch> -> float<cm> = ( * ) cmPerInch
let metersToInches: float<m> -> float<inch> = metersToCentimeters >> centimetersToInches
let metersToFeets: float<m> -> float<ft> = metersToInches >> inchesToFeets
let feetsToInches: float<ft> -> float<inch> = ( * ) inchPerFeet
let metersToHours(m: float<m>): int<h> = raise(new InvalidOperationException("Unsupported operation"))
В принципе для тестирования ваших приложений на F# вы можете обойтись без каких либо специальных библиотек. Хотя если вы, как и я, предпочитаете более стандартный подход, то вы без проблем сможете воспользоваться такими библиотеками как:
Здесь я не буду вдаваться в детали типа какой фреймворк самый лучший в мире, оставлю это на ваше усмотрение. Я отдаю предпочтение xUnit и далее буду использовать его, если ваши предпочтения не совпадают с моими то вы лего можете переключиться на вашу любимую библиотеку для тестирования.
Итак для начала добавьте в ваш проект пакеты xunit [5] и xunit.runner.visualstudio [6]
Каждый маломальский тестовый фреймворк предоставляет вам минимальный набор assert-функций. В принципе их хватает в 90% случаев, но ими не совсем удобно пользоваться. Давайте рассмотрим парочку дополнительных и удобных библиотек.
Unit
, следовательно вам нужно всегда изворачиваться и писать что-то вроде actual.Should().StartWith("S") |> ignore
.Если вы сталкиваетесь с необходимостью использовать Mock-и для тестирования вы можете воспользоваться Moq [12], но если вы ищите немного более F#-дружественного решения, вы можете воспользоваться Foq [13]. Давайте сравним в использовании эти две библиотеки.
Вызов метода в Moq:
var mock = new Mock<IFoo>();
mock.Setup(foo => foo.DoIt(1)).Returns(true);
var instance = mock.Object;
Вызов метода в Foq:
let foo = Mock<IFoo>()
.Setup(fun foo -> <@ foo.DoIt(1) @>).Returns(true)
.Create()
Сравнение аргументов в Moq:
mock.Setup(foo => foo.DoIt(It.IsAny<int>())).Returns(true);
Сравнение аргументов в Foq:
mock.Setup(fun foo -> <@ foo.DoIt(any()) @>).Returns(true)
Свойство в Moq:
mock.Setup(foo => foo.Name ).Returns("bar");
Свойство в Foq:
mock.Setup(fun foo -> <@ foo.Name @>).Returns("bar")
В зависимости от ваших нужд, вы так же можете воспользоваться известным "минимизатором Arrange фазы тестирования" и генератором заглушек — AutoFixture [14]. Так же вы можете воспользоваться другими полезностями интеграции AutoFixture с xUnit.
Итак, когда все готово, можно перейти к написанию тестов. xUnit позволяет нам использовать как стандартные классы так и определение модулей в F#, вам решать какой подход вам больше подходит. Ниже представлены примеры двух подходов.
Класс:
type ConverterTest1() =
[<Fact>]
member me.``It should convert meters to centimeters as expected``() =
let actual = 1100.0<cm> |> centimetersToMeters
test <@ actual = 11.0<m> @>
[<Fact>]
member me.``It should convert centimeters to meters as expected``() =
let actual = 20.0<m> |> metersToCentimeters
test <@ actual = 2000.00<cm> @>
Модуль:
module ConverterTest2 =
open System
[<Fact>]
let ``It should convert meters to feets as expected`` () =
let actual = 32.0<m> |> metersToFeets
test <@ actual = 104.98687664041995<ft> @>
[<Fact>]
let ``It should fail when rubbish conversion is attempted`` () =
raises<InvalidOperationException> <@ metersToHours 2.0<m> @
Выше приведенные тесты благополучно запускаются в студии и на интеграционном сервере. Благодарю за внимание. Надеюсь вам эта статья была полезной.
Автор: vba
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/net/116432
Ссылки в тексте:
[1] здесь: https://github.com/vba/fsharp.tests
[2] NUnit: http://www.nunit.org/
[3] xUnit: https://github.com/xunit/xunit
[4] MSTest: https://en.wikipedia.org/wiki/Visual_Studio_Unit_Testing_Framework
[5] xunit: https://www.nuget.org/packages/xunit/2.1.0
[6] xunit.runner.visualstudio: https://www.nuget.org/packages/xunit.runner.visualstudio/2.1.0
[7] Fluent Assertions: http://www.fluentassertions.com
[8] FsUnit: https://github.com/dmohl/FsUnit
[9] здесь: https://github.com/dmohl/FsUnit#examples
[10] Unquote: https://github.com/SwensenSoftware/unquote
[11] Quoted Expressions: https://msdn.microsoft.com/en-us/library/dd233212.aspx
[12] Moq: https://github.com/Moq/moq4
[13] Foq: https://foq.codeplex.com
[14] AutoFixture: https://github.com/AutoFixture/AutoFixture
[15] Источник: https://habrahabr.ru/post/280410/
Нажмите здесь для печати.