Azure ServiceFabric - ServiceFabric.Mocksを利用したStatefulServiceの単体テスト 

# 環境
Visual Studio 2017
Service Fabric SDK 2.6.210
ServiceFabric.Mocks 2.10

SDKとMockのバージョンが異なると実行時エラーとなる場合があります。
MockはSDKのバージョンと合ったものを利用してください。


#テスト対象
[Azure Service Fabric - Web Apiを実装してみるで実装したデータ更新処理](https://qiita.com/Yossan/items/a8a44813c7f93cd509fb#%E3%83%87%E3%83%BC%E3%82%BF%E6%9B%B4%E6%96%B0%E5%87%A6%E7%90%86)に対してテストを実装する。

```C#:HogeStatefulService.cs
public async Task Update(IEnumerable<User> users)
{
    var myDictionary = await this.StateManager.GetOrAddAsync<IReliableDictionary<int, User>>(_dictionaryName);

    using (ITransaction transaction = this.StateManager.CreateTransaction())
    {
        foreach (User user in users)
        {
            await myDictionary.AddOrUpdateAsync(transaction, user.Id, user, (key, value) => user);
        }
        await transaction.CommitAsync();
    }
}
```

# 環境の準備
##テスト用コンストラクタの追加

StateManagerのMockを利用できるように下記のコンストラクタをテスト対象のStatefulServiceに実装する。

```C#:HogeStatefulService.cs
public HogeStatefulService(StatefulServiceContext context, IReliableStateManagerReplica reliableStateManagerReplica)
            : base(context, reliableStateManagerReplica)
{}
```

##テストで利用するライブラリの準備
テストプロジェクトを作成し、Moqと ServiceFabric.MocksをNugetから取得

Package Manager Consoleで下記を実行

ServiceFabric.Mocks
```PM>  Install-Package ServiceFabric.Mocks -Version 2.1.0```

GUIでNugetから取得する場合、ServiceFabric.Mocksの"s"まで入力しないとなぜか検索に引っかからないので注意(これだけ完全一致???)


#テスト実装

##ServiceContextFactoryとStateManagerのMockを作成

```C#
    StatefulServiceContext context = MockStatefulServiceContextFactory.Default;
    MockReliableStateManager mockStateManager = new MockReliableStateManager();
```

##上記Mockを利用してテスト対象のインスタンスを生成

```C#
    HogeStatefulService target = new HogeStatefulService(context, mockStateManager);
```

##初期データを登録(必要な場合)

```C#
    IReliableDictionary<int, User> dictionary = await mockStateManager.GetOrAddAsync<IReliableDictionary<int, User>>("MyDictionary");
    IEnumerable<User> setupDataSet = new List<User>
    {
        new User{ Id = 1, FirstName = "太郎", LastName = "山田"},
        new User{ Id = 2, FirstName = "花子", LastName = "山田"}
    };
    
    using (ITransaction transaction = mockStateManager.CreateTransaction())
    {
        foreach (var setupData in setupDataSet)
        {
            await dictionary.AddAsync(transaction, setupData.Id, setupData);
        }
        await transaction.CommitAsync();
    }
```

##テストの実行

```C#
    await target.Update(new List<User> { new User { Id = 1, FirstName = "次郎", LastName = "山本" } });
```

## Assert
```C#
    using (ITransaction transaction = mockStateManager.CreateTransaction())
    {
        User result = (await dictionary.TryGetValueAsync(transaction, 1)).Value;

        Assert.AreEqual(1, result.Id);
        Assert.AreEqual("次郎", result.FirstName);
        Assert.AreEqual("山本", result.LastName);
    }
```

##テストコード全体
```C#
[TestMethod]
public async Task UpdateTest()
{
    // setup
    StatefulServiceContext context = MockStatefulServiceContextFactory.Default;
    MockReliableStateManager mockStateManager = new MockReliableStateManager();

    HogeStatefulService target = new HogeStatefulService(context, mockStateManager);
    IReliableDictionary<int, User> dictionary = await mockStateManager.GetOrAddAsync<IReliableDictionary<int, User>>("MyDictionary");
    IEnumerable<User> setupDataSet = new List<User>
    {
        new User{ Id = 1, FirstName = "太郎", LastName = "山田"},
        new User{ Id = 2, FirstName = "花子", LastName = "山田"}
    };

    using (ITransaction transaction = mockStateManager.CreateTransaction())
    {
        foreach (var setupData in setupDataSet)
        {
            await dictionary.AddAsync(transaction, setupData.Id, setupData);
        }
        await transaction.CommitAsync();
    }

    // Act
    await target.Update(new List<User> { new User { Id = 1, FirstName = "次郎", LastName = "山本" } });

    // Assert
    using (ITransaction transaction = mockStateManager.CreateTransaction())
    {
        User result = (await dictionary.TryGetValueAsync(transaction, 1)).Value;

        Assert.AreEqual(1, result.Id);
        Assert.AreEqual("次郎", result.FirstName);
        Assert.AreEqual("山本", result.LastName);
    }
}
```

#まとめ
ServiceFabric.MocksのStateManagerのMockを利用することで、StateManagerのTransaction管理やデータの永続化を利用している部分のテストが可能となる。

参考：[GitHub - ServiceFabric.Mocks](https://github.com/loekd/ServiceFabric.Mocks)