2016-04-24 42 views
10

Herşeyin başlığında olduğunu düşünüyorum ama için özel arıyorum:Ocaml'de test odaklı geliştirme nasıl yapılır?

  • ocaml içinde "standart" birim test çerçevesi nedir?
  • Yürütme testlerini yapıya nasıl eklerim?
  • Her dosya değişikliğinde otomatik olarak nasıl test yürütülür? Ben test kapsamı kalıp ilgi olacağını bir bonus olarak

...

+0

Ocaml'e bağlılık yoktur; Jenkins bunu yapmak için çerçeve sağlar. Ocaml için tst kapsamı ile ilgili olarak: Bisect (ancak camlp4'e dayanır) vardır. –

+2

Jenkins TDD için değil, sürekli bir entegrasyon motoru olduğu kadar bir mutfak lavabosu ... Düzenlenmiş soru daha hassas olması. – insitu

cevap

9

Birkaç diğer paketler kaputt veya gibi orada, paket ounit oldukça büyük ilgi görüyor gibi görünüyor broken - Ben ikincisinin yazarıyım.

Testlerin otomatikleştirilebildiği TDD'nin belirli bir parçası olarak ilgileniyorsanız, işte kendi projelerimde bunu nasıl yapacağım. Her ikisi de testsuite klasörlerinde bulunan bir test paketine sahip olan Lemonade veya Rashell gibi GitHub'da birkaç örnek bulabilirsiniz.

Genellikle göre iş akışına göre çalışır:

  1. Ben, testler ve arayüz (.mli) dosyaları aynı anda ben minimal bir programı yazmak ve yalnızca bir test vakası yazmayın bu şekilde çalışması için başlangıç Ben uygulamak istediğim fonksiyonlar, aynı zamanda kullanımı kolay bir arayüze sahip olduğumdan emin olmak için arayüzler ile deneme fırsatı da var.

Örneğin, Rashell_Posix bulundu find(1) komuta arayüzü için ben test cases yazarak başladı: spec ve find_fixture fonksiyonları verilen isim ve izinlerine sahip bir dosya hiyerarşisi oluşturmak için kullanılan

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 

find işlevini çalıştırmak için.

Eşzamanlı
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"; 
    ] 

Ben on the interface file yazıyordum:

(** 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. *) 
    Sonra assert_find fonksiyon beklenen sonuçlarla find(1) bir çağrı sonuçlarını karşılaştıran bir test durum hazırlar
  1. Test durumlarım ve arayüzlerimden memnun kaldığımda, uygulama yapmadan bile bunları derlemeye çalışabilirim. Bu, Makefile'da bir uygulama dosyası yerine bir arabirim dosyası vererek bsdowl ile mümkündür. Burada derleme, testlerimde düzeltebileceğim birkaç tip hatayı muhtemelen ortaya çıkardı.

  2. arayüzüne karşı derlenmiş testi, ben bir mazeret işlevi ile başlayan işlevi uygulamak olabilir

    :

    _ = failwith "Rashell_Posix bulalım.bulmak: Değil uygulanan '4".. Bu noktada-Elbette test sadece başarısız benim kütüphane ve benim test-suite derlemek başardı Bu uygulamayla

    Bu noktada
  1. , sadece Rashell_Posix.find işlevi uygulamak ve testler yineleme gerekiyordu.

Bu benim otomatik testler kullandığımda OCaml test güdümlü geliştirme yapmak nasıl. Bazı kişiler etkileşim görmek REPL ile test odaklı bir gelişim biçimi olarak, bu bir tekniktir. Ayrıca kullanmaktan hoşlandığım e, kurulum ve kullanım için oldukça basittir. Rashell'da bu son test odaklı geliştirme yöntemini kullanmanın tek kurulum adımı, tüm gerekli kitaplıkların üst düzeyini yüklemek için bir .ocamlinit dosyası yazmaktı.

#use "topfind";; 
#require "broken";; 
#require "lemonade";; 
#require "lwt.unix";; 
#require "atdgen";; 
#directory "/Users/michael/Workshop/rashell/src";; 
#directory "/Users/michael/obj/Workshop/rashell/src";; 

iki #directory direktifleri kaynakları ve nesneler için dizinlere karşılık gelir: gibi Bu dosya görünüyor.

(Yasal Uyarı:. Eğer tarihine dikkatlice bakarsanız, ben kronolojisi bazı özgürlükler aldı olduğunu göreceksiniz, ama ben tam olarak bu şekilde devam diğer projeleri vardır - Sadece tam hangilerinin hatırlayamıyorum)

+0

ayrıca #require "foo, bar, baz" 'ı da yapabilirsiniz –

+1

Bu doğru, ama ben her şeyi temiz bir yaraya sahip olmak için bir satır üzerine koymaya eğilimliyim. :) –

+2

Tam cevap için çok teşekkürler, tam olarak aradığım şey bu! – insitu