Разработка на коленке

"тут должна быть красивая цитата о программировании"

Тестирование OCaml-модулей при помощи Alcotest

2016-09-16 20:01
alcotest terminal

Мне нужно было научиться писать тесты на OCaml, чтобы я мог контролировать работу своих модулей. Не настолько я крутой программист, чтобы всегда обходиться без тестов, особенно это касается новых для меня языков.

Из списка библиотек для тестирования взял первый по списку - Alcotest. Когда не знаешь ничего, лучший способ разобраться - попробовать.

Для разработки первого теста взял первую задачу из списка 99 problems на ocaml.org - "Write a function last : 'a list -> 'a option that returns the last element of a list".

# last [ "a" ; "b" ; "c" ; "d" ];;
- : string option = Some "d"
# last [];;
- : 'a option = None

Т.е. нужно написать функцию, которая возвращает последний элемент списка или None.

Решение задачи

Чтобы проверить в работе Alcotest, я сперва написал решение задачи. Хоть это и не по канонам TDD, в соответствии с которыми сперва пишут тест, но меня это устраивает.

Интерфейс last.mli

val last: 'a list -> 'a option

Реализация last.ml

let rec last = function
    | [] -> None
    | [elem] -> Some elem
    | hd :: tl -> last tl

Тест test.ml

open Last


let empty_list () =
  Alcotest.(check (option reject)) "Last from empty list" None (last [])


let one_elem () =
  Alcotest.(check (option int)) "Last from empty list" (Some 1) (last [1])


let last_int () =
  Alcotest.(check (option int)) "Last int" (Some 2) (last [1; 2])


let last_char () =
  Alcotest.(check (option char)) "Last char" (Some 'a') (last ['b'; 'c'; 'a'])


let last_string () =
  Alcotest.(check (option string)) "Last string" (Some "hi") (last ["bye"; "hi"])


let () =
  Alcotest.run "Test Last.last function which should return last element of a list" [
      "test_set", [
        "Get last from empty list", `Quick, empty_list;
        "Get the only element from list", `Quick, one_elem;
        "Get the int from list", `Quick, last_int;
        "Get the char from list", `Quick, last_char;
        "Get the string from list", `Quick, last_string;
      ]
  ]

Сборка

$ ocamlbuild -pkg alcotest test.byte
Finished, 7 targets (7 cached) in 00:00:00.

И результат в терминале

$ ./test.byte
Testing Test Last.last function which should return last element of a list.
[OK]                test_set          0   Get last from empty list.
[OK]                test_set          1   Get the only element from list.
[OK]                test_set          2   Get the int from list.
[OK]                test_set          3   Get the char from list.
[OK]                test_set          4   Get the string from list.
The full test results are available in `_tests`.
Test Successful in 0.002s. 5 tests run.

Все тесты пройдены, все танцуют.

Эмуляция ошибки

На самом деле нужно было с этого начинать разработку, но не в том случае, если для меня предметом изучения является сама библиотека для тестов.

Сперва я поломал функцию last

let rec last = function
    | _ -> None

Потом пересобрал тест и запустил

$ ocamlbuild -pkg alcotest test.byte
Finished, 7 targets (4 cached) in 00:00:00.
$ ./test.byte
Testing Test Last.last function which should return last element of a list.
[OK]                test_set          0   Get last from empty list.
[ERROR]             test_set          1   Get the only element from list.
[ERROR]             test_set          2   Get the int from list.
[ERROR]             test_set          3   Get the char from list.
[ERROR]             test_set          4   Get the string from list.
The full test results are available in `_tests`.
4 errors! in 0.003s. 5 tests run.

Ну и славно, значит ошибки искать тоже умеет. А ещё он умеет раскрашивать вывод.

alcotest terminal 2

Заключение

Alcotest на текущий момент меня устраивает, поэтому сейчас буду тестировать свой код при помощи этой библиотеки. Для моих скромных потребностей её хватает.