F#でNUnit+FsUnit+Moqを使って単体テストをする

# はじめに
F#を使ってユニットテストしたいですよね。  
.NETでmockと言えばMoqですよね。  
Moqを使ってユニットテストを試してみましょう。

# 前提
テスト用のプロジェクト作成からやっていきます。  
dotnetのコマンドをぽちぽち打ちながらやっていきます。

# 下準備
## ソリューション作成
今回は`NUnitMoq`というソリューションを作ります。  
その下にテスト対象のプロジェクトとテストプロジェクトを作っていきます。  
```bash
mkdir NUnitMoq
cd NUnitMoq

# ソリューション作成
dotnet new sln
```

## プロジェクト作成
テスト対象のプロジェクトとテストプロジェクトを作ります。  
```bash
# テスト対象のプロジェクト作成
dotnet new console -lang="F#" -o="Sample"
# テストプロジェクトを作成
dotnet new nunit -lang="F#" -o="SampleTest"
```

それぞれのプロジェクトをソリューションに紐付けます。  
```bash
dotnet sln NUnitMoq.sln add Sample
dotnet sln NUnitMoq.sln add SampleTest
```

テストプロジェクトからテスト対象プロジェクトへの参照を追加します。  
```bash
dotnet add SampleTest reference Sample
```

## FsUnitとMoqの依存を追加
SampleTest配下でこれをする。
```bash
dotnet add package FsUnit --version 5.2.0
dotnet add package Moq --version 4.18.4
```

# テスト対象
```fsharp:TestTarget.fs
module TestTarget

open System
open GetDataPort

let getDataWithUpperLetter (getData: GetData) (dataType: DataType) = 
    getData dataType |> String.map Char.ToUpper
```

```fsharp:GetDataPort.fs
module GetDataPort

type DataType = TypeA | TypeB
type GetData = DataType -> string
```

```fsharp:GetData.fs
module GetData

open GetDataPort

let getData (x: DataType) = 
    match x with
        | TypeA -> "hoge"
        | TypeB -> "piyo"
```

テスト対象になるのは`TestTarget.fs`の`getDataWithUpperLetter`です。  
`GetDataPort`の`GetData`を引数として受け取っています。  
今回のテストでは、GetDataをmockしていきます。  
※GetDataのイメージが付きやすくなるように、`GetData.fs`のソースものっけましたが、今回のテストをするだけなら`GetData.fs`は不要です

# テストを書いてみる
```fsharp:GetDataWithUpperLetterTest.fs
open System
open NUnit.Framework
open FsUnit
open Moq
open TestTarget
open GetDataPort

[<TestFixture>]
module GetDataWithUpperLetterTest =
    [<Test>]
    let ``Moqを使ってテストするぞ``() =
        // モックの作成
        let getDataMock = new Mock<GetData>()

        // モックのセットアップ
        getDataMock.Setup(fun x -> x DataType.TypeA).Returns "Hello World"

        // モックのオブジェクトを取得
        let getData = getDataMock.Object

        // テスト実行
        getDataWithUpperLetter getData DataType.TypeA |> should equal "HELLO WORLD"

        // モックが期待通り動いたか確認
        getDataMock.VerifyAll()
```
SampleTest配下で`dotnet test`を打つとテストが実行されます。  
`VerifyAll()`はセットアップしたモックが正しく呼ばれているかなどを確認できる。

# 感想
一部C#っぽ記法が出ちゃうのは残念ですが、mockのパッケージを使えるのはありがたい。
色々端折ったところが多いので、ソース見たい方は下記を参照してください。  

https://github.com/k2491p/nunit-fsunit-moq-example

# 追記
モックが呼ばれていないことは`VerifyNoOtherCalls`で確認できそう
```fsharp
open System
open NUnit.Framework
open FsUnit
open Moq
open TestTarget
open GetDataPort

[<TestFixture>]
module GetDataWithUpperLetterTest =
    [<Test>]
    let ``Moqを使ってテストするぞ``() =
        // モックの作成
        let getDataMock = new Mock<GetData>()
        // モックのセットアップ
        getDataMock.Setup(fun x -> x DataType.TypeA).Returns "Hello World"

        // モックのオブジェクトを取得
        let getData = getDataMock.Object

        // 呼ばれない予定のモック
        let notCallMock = new Mock<NotCall>()
        let notCall = notCallMock.Object

        // テスト実行
        getDataWithUpperLetter getData DataType.TypeA notCall |> should equal "HELLO WORLD"

        // モックが期待通り動いたか確認
        getDataMock.VerifyAll()
        // モックが呼ばれていないことの確認
        notCallMock.VerifyNoOtherCalls()
```

# 参考
- [F#でxUnitを使う方法](https://qiita.com/Midoliy/items/29abc75c296d8cce7137#%E3%83%86%E3%82%B9%E3%83%88%E5%AF%BE%E8%B1%A1%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AB%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B)
- [F#入門|FsUnit](http://fsharpintro.net/fsunit.html)
- [FsUnit for NUnit](https://fsprojects.github.io/FsUnit/NUnit.html)
- [C# で Moq でモックを使ったテストをする](https://rksoftware.hatenablog.com/entry/2021/12/02/020344)