C# で湯婆婆を Windows アプリと WebAssembly 対応してみた（無駄に MVVM）

最近流行していて、さらにはアドベントカレンダーまで登場した湯婆婆ですが、[Flutterで湯婆婆を実装してみる](https://qiita.com/simontanz/items/50c06aaaac3aa9c896db) に触発されて湯婆婆の画面付きを作ってみました。

こんな感じです。左が Windows アプリで右側が WebAssembly になります。

![yubaba.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/c9d8fa8b-cd70-805d-9448-c7b96cfd04db.gif)

実際に動くサイトは以下にデプロイしています。

[湯婆婆 on Azure](https://yubaba.z11.web.core.windows.net/)

## 使ってるもの

Windows, Android, iOS, WebAssembly, macOS, Linux に対応している Uno Platform を使って作りました。今回はスマホ対応は単純にレスポンシブに作ることがメンドクサカッタのと、macOS と Linux は環境を用意するのがメンドクサカッタので Windows と WebAssembly を対象に作りました。

Uno Platform については以前 Qiita で紹介しました。

[Uno Platform 入門](https://qiita.com/okazuki/items/c548a834e29d618555d8)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/289de08e-71f6-61f8-8881-17b355dfde22.png)

公式サイトはこちらになります。

[Uno Platform](https://platform.uno/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/92195789-7301-1f33-9c03-ea8dd9e0731b.png)

## 無駄に頑張った所

今回の湯婆婆を作るうえで頑張ったところを列挙していきたいと思います。

### 𠮷田さん対応

サロゲートペアに対応しています！！C# での対応方法は先日自分で書いたのでその通りにやりました。

[C# で湯婆婆を実装してみる（𠮷田さんにも対応）](https://qiita.com/okazuki/items/03049da368222b562da9)

### 無駄に MVVM

湯婆婆ごときにはオーバースペックなのでお勧めしませんが、なんとなく先日 Uno Platform に対応した Prism (MVVM フレームワーク) と ReactiveProperty を使って開発しています。

なのでソリューションエクスプローラーはこんなに沢山プロジェクトがあります。オーバースペック！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/2b9e222f-4f71-bbb7-088a-282ad73c8bb3.png)

### 単体テスト書いた

湯婆婆のコアロジック（ちゃんとしたセリフになってるか）を単体テストしました。ランダムに 1 文字選ぶために乱数を生成している部分は乱数生成部分をモックに差し替え可能なように作って単体テストではモックを差し込んでいます。

テスト結果が緑だと気持ちいいですね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70288/e87ad0a5-9436-7f22-3d6e-473cb980b48d.png)

𠮷田さん対応のテストケースも書いています。あと大事な仕様の名前が未入力だと湯婆婆が死ぬことも確認しています。

```csharp
[Fact]
public async Task SurrogatePairName()
{
    MockRandom.Setup(x => x.Next(2)).Returns(0).Verifiable();
    await Target.RequestIntroductionAsync();
    await Target.SubmitContractAsync(new ContractPaper("𠮷田"));
    Assert.Collection(Target.Messages,
        act => Assert.Equal("フン。", act.Value),
        act => Assert.Equal("𠮷田というのかい。", act.Value),
        act => Assert.Equal("贅沢な名だねぇ。", act.Value),
        act => Assert.Equal("今からお前の名前は𠮷だ。", act.Value),
        act => Assert.Equal("いいかい、𠮷だよ。", act.Value),
        act => Assert.Equal("分かったら返事をするんだ、𠮷!!", act.Value));
    MockRandom.VerifyAll();
}

[Fact]
public async Task KillYubabaUsingNull()
{
    MockRandom.Setup(x => x.Next(2)).Returns(0).Verifiable();
    await Target.RequestIntroductionAsync();
    await Assert.ThrowsAsync<ArgumentNullException>(
        async () => await Target.SubmitContractAsync(new ContractPaper(null)));
}

[Fact]
public async Task KillYubabaUsingEmpty()
{
    MockRandom.Setup(x => x.Next(2)).Returns(0).Verifiable();
    await Target.RequestIntroductionAsync();
    await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
        async () => await Target.SubmitContractAsync(new ContractPaper("")));
}
```

## まとめ

ついかっとなって画面を作ってしまいました。ちょうど Uno Platform 版の Prism を試してみたかったので個人的には満足です。

ソースコードは以下のリポジトリで公開しています。

https://github.com/runceel/Yubaba.Uno