При написании тестов на F# я пытаюсь генерировать полезные сообщения о состоянии, которое вызвало ошибки. В python я бы включил все locals(), чтобы они были легко доступны в тестовой трассировке.
Есть ли аналогичная конструкция в F#?
Я искал в Интернете и на отличном сайте fsharpforfunandprofit, а также просмотрел список зарезервированных ключевых слов.
Вот некоторый код, который я хотел бы сделать.
[<Property>]
let some_test x =
let example_inner_helper y =
let z = y + x
// Example of the meta-construction i am looking for
let scope = {|
local = {| y = y; z = z |};
capture = {|
local = {| x = x |};
capture = {| (* ... *) |};
|};
|}
x = z |@ sprintf "require %A=%A (%A)" x y scope
example_inner_helper (x+x)
Что даст очень полезный результат
Tests.some_test
Source: Tests.fs line 105
Duration: 51 ms
Message:
FsCheck.Xunit.PropertyFailedException :
Falsifiable, after 1 test (1 shrink) (StdGen (387696160,296644521)):
Label of failing property: require 1=2 ({ capture = { capture = {}
local = { x = 1 } }
local = { y = 2
z = 3 } })
Original:
-1
Shrunk:
1
Однако мне приходится явно фиксировать информацию, которую может автоматически предоставить такая конструкция, как «область действия». В результате получаются уродливые, подверженные ошибкам вещи, такие как
let ``firstAscendingLastDescendingPartE Prop`` (a: Extent<int>) b =
let validateT ea eb =
let checkXbeforeY ex ey headx restx =
match restx with
| ValueNone -> // No rest
(ex = headx) |@ sprintf "no rest: ex = headx (%A = %A)" ex headx
.&. ((intersectE ex ey) = ValueNone) |@ sprintf "no rest: ex ∩ ey = ∅ (%A ∩ %A)" ex ey
| ValueSome rex -> // Some rest ->
// headx and restx combines to ex
((expandE headx rex) = ex) |@ sprintf "rest: headx+rex = ex (%A...%A)" headx rex
.&. (exactlyTouchesE headx rex) |@ sprintf "rest: headx ends where rex starts (ex=%A ey=%A headx=%A restx=%A)" ex ey headx restx
.&. (exactlyTouchesE headx ey) |@ sprintf "rest: headx ends where ey starts (ex=%A ey=%A headx=%A restx=%A)" ex ey headx restx
....
(Конечно, меня не волнует фактический тип конструкции "scope").
Что касается отмены кавычек
Я уже посмотрел на unquote, который довольно симпатичный и выглядит примерно так. Но это немного ограничивает код:
- Ошибка FS3155 Цитата не может включать присвоение или получение адреса захваченной локальной переменной.
- Ошибка FS1230 Внутренние универсальные функции не разрешены в выражениях в кавычках. Рассмотрите возможность добавления некоторых ограничений типа, пока эта функция не перестанет быть универсальной.
У меня есть код, который выглядит примерно так:
[<Struct>]
type X<'T when 'T: comparison and 'T: equality> =
val public first: 'T
val public last: 'T
new (first: 'T, last: 'T) = {
first =
(if last <= first then
invalidOp (sprintf "first < last required, (%A) is not < (%A)" first last)
else first)
last = last;
}
// ..
Поэтому у меня есть проблемы с тестами, использующими две приведенные ниже конструкции (вызывающие вышеуказанные ошибки).
let quote_limits () =
let x first last = X(first, last)
let validate (x: X<'a>) = x.first < x.last
let a = X(0, 1)
<@ a.first = 0 @> |> test
<@ validate a @> |> test
Первый, который я могу обойти с помощью функций для доступа к частям структуры, но ограничение на дженерики — это PITA.