shuhelohelo’s blog

Xamarin.Forms多めです.

Fluent Assertionを使う

環境

はじめに

FluentAssertionsはC#単体テストフレームワークです。

このフレームワークを使うことで単体テストのコードがどのように書けるのかは、公式サイトの説明を見たほうがよいと思います。

ぱっと見た感じだと、英語の構文に近い感じでメソッドをつなぎ合わせてテストコードを記述できるようです。

おしゃれですね。

インストール

インストールは簡単お手軽なNugetから。 FluentAssertionsと検索すると出てきます。 これをテストプロジェクトに☑をつけてインストールします。

f:id:shuhelohelo:20190823210529p:plain

やってみよう

では早速やってみます。

練習1

やはりここは先ほどのGetting Startedからやってみるのが分かりやすいのだろうと思います。

何はなくともusing FluentAssertions;を追加します。

そして文字列に対するテストを書きます。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using FluentAssertions;//追加

namespace TestProject
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            string actual = "ABCDEFGHI";
            actual.Should().StartWith("AB").And.EndWith("HI").And.Contain("EF").And.HaveLength(9);//追加
        }
    }
}

テストコードを見てみると、「Actual should start with ... and end with ... and contain ... and have length ...」というようにまるで英語です。

このように自然言語に近い形で書けるため、それがどういうテストを行うものなのかがわかりやすくなっています(個人の感想です)。

しかし、その分コードとしては長くなってしまいますね。

練習2

次は配列に対するテストです。 配列には4つの要素が入っているべき、というテストです。

        [TestMethod]
        public void TestMethod2()
        {
            IEnumerable<int> numbers = new int[] { 1, 2, 3 };//3つしか入れないよ!
            numbers.Should().HaveCount(4, "ここにメッセージ:4つ数字を入れたと思ったんだけれど…(´・ω・`)");
        }

当然これはテストに失敗しますが、失敗した項目を選択すると以下のような情報を見ることができます。

f:id:shuhelohelo:20190823213213p:plain

  Message: 
    Expected numbers to contain 4 item(s) because ここにメッセージ:4つ数字を入れたと思ったんだけれど…(´・ω・`), but found 3.

こんな感じでテスト失敗時のメッセージを指定、表示できますし、こけた理由をわかりやすくすることができます。

練習3

次はよくある足し算メソッドに対するテストを書いてみましょう。

まずはテストコードを書きます。

        [TestMethod]
        public void TestMethod3()
        {
            //よくある足し算メソッドのテスト
            Calculate.Add(1, 2).Should().Be(3, "3が返ってくるはず");
        }

テストしたいメソッドの結果に対してそのままShould().Be(3)とつなげて書くことができるので、コードの意味が分かりやすいです。

「このメソッドの返り値 Should be 3!」

という強い意志が伝わります。

次にクラスとメソッドを追加してテストを実行します。

    public class Calculate
    {
        public static int Add(int a, int b)
        {
            return 0;//とりあえず0返す
        }
    }

素晴らしい!失敗しましたね!

f:id:shuhelohelo:20190823214341p:plain

練習4

最後は日付、DateTimeDateTimeOffsetに関するテストです。(こちら

日付の記述が独特ですがHuman readableを貫いていて好感が持てます。(個人の感想です)

ちょっと以下のようなコードを書いて、得られる値を確認してみました。

using FluentAssertions.Extensions;//この2つをusingに追加
using FluentAssertions.Common;
//~~~~~~~~~~~~

var theDateTime = 23.August(2019).At(22, 01, 34).ToDateTimeOffset(9.Hours());

これは以下のようなDateTimeOffset型の日付を表しています。

f:id:shuhelohelo:20190823220609p:plain

「23 Aug 2019 22:01:34 +09:00」という英語まんまです。

この日付に関するメソッド群はDateTimeクラスの拡張メソッドとして定義されています。

それではこの日付に対して金曜日であることをテストしてみます。

ここまでの経験上、きっとこう書くはずです。よゆーです。

‘‘‘cs theDateTime.Should().BeFriday();

[f:id:shuhelohelo:20190823222059p:plain]


なん…だと…!?


ある日付が金曜日かどうかを判定するメソッドが用意されていないなんて、これは由々しき事態です。解決しなければ。

public static class DayOfWeekAssertions
{
    public static AndConstraint<DateTimeOffsetAssertions> BeFriday(this DateTimeOffsetAssertions instance, string because = "", params object[] becauseArgs)
    {
        var subjectDayOfWeek = instance.Subject.Value.DayOfWeek;
        Execute.Assertion
            .ForCondition(subjectDayOfWeek == DayOfWeek.Friday)
            .BecauseOf(because, becauseArgs)
            .FailWith("Expect {context:the day of week} to be Friday{reason}, but it is {0} ", subjectDayOfWeek);

        return new AndConstraint<DateTimeOffsetAssertions>(instance);
    }
}
そしてテストメソッドは、こちら。
「その日は Should Be Friday!」
    [TestMethod]
    public void TestMethod4()
    {
        var theDateTime = 22.August(2019).At(22, 01, 34).ToDateTimeOffset(9.Hours());//8月22日(木曜日)にした
        theDateTime.Should().BeFriday("今日は金曜日だと思ったの…(´・ω・`)");
    }
[f:id:shuhelohelo:20190824002358p:plain]


無事に金曜日じゃないと判定されました。

これで金曜日が「金曜日」であることを保証できますね。

それではよい週末を!


# おまけ

任意の日付が任意の曜日であることをテストするメソッドは以下のとおりです。
public static class DayOfWeekAssertions
{
    public static AndConstraint<DateTimeOffsetAssertions> Be(this DateTimeOffsetAssertions instance, DayOfWeek expected, string because = "", params object[] becauseArgs)
    {
        var subjectDayOfWeek = instance.Subject.Value.DayOfWeek;
        Execute.Assertion
            .ForCondition(subjectDayOfWeek == expected)
            .BecauseOf(because, becauseArgs)
            .FailWith("Expect {context:the day of week} to be {0}{reason}, but it is {1} ", expected, subjectDayOfWeek);

        return new AndConstraint<DateTimeOffsetAssertions>(instance);
    }
}
Fluent Assertionsのソースコードは[GitHub](https://github.com/fluentassertions/fluentassertions)で公開されているので、参考にするとオレオレAssertionが作れます。