Ответ 1
Кажется, что пакет ounit пользуется большой популярностью, есть несколько других пакетов, таких как kaputt или broken. Я являюсь автором последняя.
Я думаю, вас интересует как часть TDD, где тесты могут быть автоматизированы, вот как я это делаю в своих проектах. Вы можете найти несколько примеров на GitHub, таких как Lemonade или Rashell, которые имеют тест набор найден в соответствующих папках testsuite
.
Обычно я работаю в соответствии с соответствующим рабочим процессом:
- Я начинаю работать одновременно на тестах и интерфейсах (
.mli
), таким образом, я пишу минимальную программу и не только пишу тестовый пример для функций, которые я хочу реализовать, но и имею возможность экспериментировать с интерфейсами чтобы иметь удобный интерфейс.
Например, для интерфейса с командой find(1)
, найденной в Rashell_Posix, я начал с написания тестовых примеров:
open Broken
open Rashell_Broken
open Rashell_Posix
open Lwt.Infix
let spec base = [
(true, 0o700, [ base; "a"]);
(true, 0o750, [ base; "a"; "b"]);
(false, 0o600, [ base; "a"; "b"; "x"]);
(false, 0o640, [ base; "a"; "y" ]);
(true, 0o700, [ base; "c"]);
(false, 0o200, [ base; "c"; "z"]);
]
let find_fixture =
let filename = ref "" in
let cwd = Unix.getcwd () in
let changeto base =
filename := base;
Unix.chdir base;
Lwt.return base
in
let populate base =
Toolbox.populate (spec base)
in
make_fixture
(fun () ->
Lwt_main.run
(Rashell_Mktemp.mktemp ~directory:true ()
>>= changeto
>>= populate))
(fun () ->
Lwt_main.run
(Unix.chdir cwd;
rm ~force:true ~recursive:true [ !filename ]
|> Lwt_stream.junk_while (fun _ -> true)))
let assert_find id ?expected_failure ?workdir predicate lst =
assert_equal id ?expected_failure
~printer:(fun fft lst -> List.iter (fun x -> Format.fprintf fft " %S" x) lst)
(fun () -> Lwt_main.run(
find predicate [ "." ]
|> Lwt_stream.to_list
|> Lwt.map (List.filter ((<>) "."))
|> Lwt.map (List.sort Pervasives.compare)))
()
lst
Функции spec
и find_fixture
используются для создания иерархии файлов с указанными именами и разрешениями для реализации функции find
. Затем функция assert_find
подготавливает тестовый пример, сравнивая результаты вызова с find(1)
с ожидаемыми результатами:
let find_suite =
make_suite ~fixture:find_fixture "find" "Test suite for find(1)"
|& assert_find "regular" (Has_kind(S_REG)) [
"./a/b/x";
"./a/y";
"./c/z";
]
|& assert_find "directory" (Has_kind(S_DIR)) [
"./a";
"./a/b";
"./c"
]
|& assert_find "group_can_read" (Has_at_least_permission(0o040)) [
"./a/b";
"./a/y"
]
|& assert_find "exact_permission" (Has_exact_permission(0o640)) [
"./a/y";
]
Одновременно я писал в файле интерфейса:
(** The type of file types. *)
type file_kind = Unix.file_kind =
| S_REG
| S_DIR
| S_CHR
| S_BLK
| S_LNK
| S_FIFO
| S_SOCK
(** File permissions. *)
type file_perm = Unix.file_perm
(** File status *)
type stats = Unix.stats = {
st_dev: int;
st_ino: int;
st_kind: file_kind;
st_perm: file_perm;
st_nlink: int;
st_uid: int;
st_gid: int;
st_rdev: int;
st_size: int;
st_atime: float;
st_mtime: float;
st_ctime: float;
}
type predicate =
| Prune
| Has_kind of file_kind
| Has_suffix of string
| Is_owned_by_user of int
| Is_owned_by_group of int
| Is_newer_than of string
| Has_exact_permission of int
| Has_at_least_permission of int
| Name of string (* Globbing pattern on basename *)
| And of predicate list
| Or of predicate list
| Not of predicate
val find :
?workdir:string ->
?env:string array ->
?follow:bool ->
?depthfirst:bool ->
?onefilesystem:bool ->
predicate -> string list -> string Lwt_stream.t
(** [find predicate pathlst] wrapper of the
{{:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html} find(1)}
command. *)
-
Как только я был доволен своими тестовыми примерами и интерфейсами, я мог бы попытаться их скомпилировать, даже без реализации. Это возможно с bsdowl, просто предоставив файл интерфейса вместо файла реализации в Makefile. Здесь компиляция, вероятно, обнаружила несколько ошибок типа в моих тестах, которые я мог бы исправить.
-
Когда тест скомпилирован для интерфейса, я мог бы реализовать эту функцию, начиная с функции alibi:
найдем _ = failwith "Rashell_Posix.find: не реализовано"
С этой реализацией я смог скомпилировать мою библиотеку и свой тестовый пакет. Конечно, в этот момент тест просто терпит неудачу.
- В этот момент мне просто нужно было реализовать функцию
Rashell_Posix.find
и повторить тесты до тех пор, пока они не пройдут.
Вот как я делаю тестовую разработку в OCaml, когда я использую автоматические тесты. Некоторые люди видят взаимодействие с REPL как форму тестового развития, это техника, которую я также люблю использовать, ее довольно просто настроить и использовать. Единственный шаг настройки для использования этой последней формы тестовой разработки в Rashell состоял в том, чтобы написать файл .ocamlinit
для верхнего уровня, загружая все необходимые библиотеки. Этот файл выглядит так:
#use "topfind";;
#require "broken";;
#require "lemonade";;
#require "lwt.unix";;
#require "atdgen";;
#directory "/Users/michael/Workshop/rashell/src";;
#directory "/Users/michael/obj/Workshop/rashell/src";;
Две директивы #directory
соответствуют каталогам источников и объектов.
(Отказ от ответственности: если вы внимательно посмотрите на историю, вы обнаружите, что я взял некоторые свободы с хронологией, но есть и другие проекты, в которых я выполняю именно этот путь - я точно не помню, какие именно).