SAFE Stack (link) のサンプルの ToDo アプリを眺める続きの続き。 テストのソースを見る。

対象のファイルは以下の 3 個。

ファイル行数
tests/Client/Client.Tests.fs28
tests/Server/Server.Tests.fs27
tests/Shared/Shared.Tests.fs15
合計70

tests/Client/Client.Tests.fs

1	module Client.Tests
2	
3	open Fable.Mocha
4	
行番号コメント
3クライアントのテストは Fable.Mocha を利用する。サーバのテストで利用する Expecto と共通のテストコードを書けるとある。
 5	open Index
 6	open Shared
 7	
 8	let client = testList "Client" [
 9	    testCase "Added todo" <| fun _ ->
10	        let newTodo = Todo.create "new todo"
11	        let model, _ = init ()
12	
13	        let model, _ = update (AddedTodo newTodo) model
14	
15	        Expect.equal 1 model.Todos.Length "There should be 1 todo"
16	        Expect.equal newTodo model.Todos.[0] "Todo should equal new todo"
17	]
18	
行番号コメント
8testList の第2引数はリストで testCase やネストした testList を要素にもてる。
11init 関数でモデルを初期化。init 関数の中で作っている初期コマンド ( Cmd.OfAsync.perform todosApi.getTodos () GotTodos ) は無視していて、サーバの API も呼び出していない。モデルの ToDo 項目は空リストのまま。
13update 関数でメッセージを処理。本来、AddedTodo メッセージはサーバへの API 呼び出しが成功したときに返されるものだけど、ここでは直接 update 関数に渡している。
15, 16更新後のモデルを検査。
19	let all =
20	    testList "All"
21	        [
22	#if FABLE_COMPILER // This preprocessor directive makes editor happy
23	            Shared.Tests.shared
24	#endif
25	            client
26	        ]
27	
28	[<EntryPoint>]
29	let main _ = Mocha.runTests all
行番号コメント
22コメントの This preprocessor directive makes editor happy の理由が分からない。
23, 25testList の中にネストして別の testList を格納している。
29テストを実行。

tests/Server/Server.Tests.fs

1	module Server.Tests
2	
3	open Expecto
4	
行番号コメント
3サーバのテストは Expecto を利用する。
 5	open Shared
 6	open Server
 7	
 8	let server = testList "Server" [
 9	    testCase "Adding valid Todo" <| fun _ ->
10	        let storage = Storage()
11	        let validTodo = Todo.create "TODO"
12	        let expectedResult = Ok ()
13	
14	        let result = storage.AddTodo validTodo
15	
16	        Expect.equal result expectedResult "Result should be ok"
17	        Expect.contains (storage.GetTodos()) validTodo "Storage should contain new todo"
18	]
19	
行番号コメント
10-17この ToDo アプリでは、サーバの storage とクライアントのモデルが大体同等で、テストもほぼ同じようなことをやろうとしている。ただ、サーバの storage が mutable、クライアントのモデルが immutable という違いがある。
20	let all =
21	    testList "All"
22	        [
23	            Shared.Tests.shared
24	            server
25	        ]
26	
27	[<EntryPoint>]
28	let main _ = runTests defaultConfig all
行番号コメント
23サーバでは Shared.Tests.shared は無条件に参照している。クライアントが #if#endif で括っていた理由がますます分からなくなった。
28テストを実行。

tests/Shared/Shared.Tests.fs

 1	module Shared.Tests
 2	
 3	#if FABLE_COMPILER
 4	open Fable.Mocha
 5	#else
 6	open Expecto
 7	#endif
 8	
 9	open Shared
10	
11	let shared = testList "Shared" [
12	    testCase "Empty string is not a valid description" <| fun _ ->
13	        let expected = false
14	        let actual = Todo.isValid ""
15	        Expect.equal actual expected "Should be false"
16	]
行番号コメント
3-7クライアントでは Fable.Mocha、サーバでは Expecto を利用する。
12-15バリデータが空の入力を弾くかテスト。