Одним из способов избежать необходимости явного написания названия теста в коде (в виде строки) было бы использование кавычек.Вместо того, чтобы создавать список функций и список строк, вы можете создать список «указанных» значений функций.Затем вы можете написать код, который обрабатывает цитаты и дает вам все необходимое.
Я предполагаю, что ваши тесты выглядят примерно так, как показано ниже (функции, получающие единицу и возвращающие некоторое значение в результате).Список будет построен следующим образом:
let test_a () = Some 32
let test_b () = None
let tests = [ <@ test_a @>; <@ test_b @> ]
Затем вы можете написать код, подобный этому, чтобы получить информацию о тесте:
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
let getInfo (e:Expr<unit -> 'R>) = // '
match e.Raw with
// This expects that the quotation contains a reference to a global
// function (in some module) that takes unit as the parameter
| Lambda(a, Call(_, m, _)) ->
// Return a tuple containing a name of the test (string) and
// a function that invokes it (note that the invocation will be
// a bit slow as we use reflection here)
m.Name, (fun () -> m.Invoke(null, [| |]) :?> 'R) // ' (*)
// Ohter quotations will cause an exception
| _ -> failwith "unexpected quotation"
Вот пример, как вы могли бы использовать это:1009 *
let e = <@ test_a @>
let s, f = getInfo e // gives 'string * (unit -> int option)
// Your original code could be written like this:
tests |> List.map getInfo |> List.tryPick (fun (title, test) ->
test () |> Option.map (fun s -> (title, s)))
В качестве альтернативы вы можете изменить строку (*)
, чтобы получить функцию, которая возвращает имя теста и результат, что устраняет необходимость в Option.map
:
// An alternative version of the marked line in the 'getInfo' function
(fun () -> m.Name, m.Invoke(null, [| |]) :?> 'R) // ' (*)
// Then you can write just:
tests |> List.map getInfo |> List.tryPick (fun test -> test())