Cadence Testing Framework

[Previous << Measuring Time In Cadence](https://qiita.com/flowcadence/items/1aefd14bce23a00518ec)
[Next >> Flow CLI](https://qiita.com/flowcadence/items/5ead732fad703556487d)

https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a#cadenceの言語仕様を学ぶ

Cadence テストフレームワークは、Cadence プログラムのテストを Cadence で記述する便利な方法を提供します。 この機能は、組み込みの`Test`スマートコントラクトによって提供されます。

:::note
Tip💡
テストフレームワークは、[Flow CLI](https://qiita.com/flowcadence/items/5ead732fad703556487d) などのオフチェーンでのみ使用できます。
:::
テストは、Cadence スクリプトの形式で記述する必要があります。 テストスクリプトには、`test`プレフィックスで始まるテスト関数、テストの前に必ず実行される`setup`関数、すべてのテストケースの最後に必ず実行される`tearDown`関数、各テストケースの前に実行される`beforeEach`関数、各テストケースの後に実行される`afterEach`関数を含めることができます。 上記の 4 つの関数はすべてオプションです。

```ruby
/* `setup`関数はその他のテストケースの前に常に実行される。テスト全体で使われるようなものを初期化するために使うことができる。 */
access(all)
fun setup() {
}

/* `beforeEach` 関数は各テストケースの前に実行されます。 */
access(all)
fun beforeEach() {
}

/* `afterEach` 関数は各テストケースの後に実行されます。 */
access(all)
fun afterEach() {
}

/* 「test」という接頭辞で始まるものが有効なテスト関数です。 */
access(all)
fun testSomething() {
}

access(all)
fun testAnotherThing() {
}

access(all)
fun testMoreThings() {
}

/* テスト関数には、引数や戻り値を指定することはできません。 */
access(all)
fun testInvalidSignature(message: String): Bool {
}

/* すべてのテストケースの最後に必ず実行される `tearDown` 関数。 */
access(all)
fun tearDown() {
}
```
<h1 id="test-standard-library">Test Standard Library</h1>

テストフレームワークは、組み込みの `Test`スマートコントラクトをインポートすることで使用できます。

```ruby
import Test
```

<h1 id="assertions">Assertions</h1>

<h3 id="test-assert">Test.assert</h3>

```ruby
view fun assert(_ condition: Bool, message: String)
```

与えられた条件が偽の場合にテストケースを失敗させ、その理由を説明するメッセージを報告する。 message 引数はオプションである。

<h3 id="test-fail">Test.fail</h3>

```ruby
view fun fail(message: String)
```
テストケースを即座に失敗させ、その理由を説明するメッセージを表示します。
メッセージ引数はオプションです。

```ruby
import Test

access(all)
fun testExample() {
    let array = [1, 2, 3]

    if array.length != 0 {
        Test.fail(message: "Array length is not 0")
    }
}
```

<h3 id="test-expect">Test.expect</h3>

```ruby
fun expect(_ value: AnyStruct, _ matcher: Matcher)
```
`expect`関数は、値をマッチャー（[matchers](#matchers)のセクション参照）に対してテストし、一致しない場合はテストに失敗します。
```ruby
import Test

access(all)
fun testExample() {
    let array = [1, 2, 3]

    Test.expect(array.length, Test.equal(3))
}
```

<h3 id="test-assert-equal">Test.assertEqual</h3>

```ruby
fun assertEqual(_ expected: AnyStruct, _ actual: AnyStruct)
```
`assertEqual`関数は、与えられた値が等しくない場合にテストケースを失敗させ、2つの値がどのように異なるかを説明するメッセージを表示します。
```ruby
import Test

access(all)
struct Foo {

    access(all)
    let answer: Int

    init(answer: Int) {
        self.answer = answer
    }
}

access(all)
fun testExample() {
    Test.assertEqual("this string", "this string")
    Test.assertEqual(21, 21)
    Test.assertEqual(true, true)
    Test.assertEqual([1, 2, 3], [1, 2, 3])
    Test.assertEqual(
        {1: true, 2: false, 3: true},
        {1: true, 2: false, 3: true}
    )

    let address1 = Address(0xf8d6e0586b0a20c7)
    let address2 = Address(0xf8d6e0586b0a20c7)
    Test.assertEqual(address1, address2)

    let foo1 = Foo(answer: 42)
    let foo2 = Foo(answer: 42)

    Test.assertEqual(foo1, foo2)

    let number1: Int64 = 100
    let number2: UInt64 = 100
    /* Note that the two values need to have exactly the same type,
     * and not just value, otherwise the assertion fails:
     * assertion failed: not equal: expected: 100, actual: 100 */
    Test.assertEqual(number1, number2)
}
```

<h3 id="test-assert-expect-failure">Test.expectFailure</h3>

```ruby
fun expectFailure(_ functionWrapper: ((): Void), errorMessageSubstring: String)
```
`expectFailure`関数は、関数呼び出しをクロージャでラップし、指定したエラーメッセージ部分を含むエラーメッセージで失敗することを期待します。
```ruby
import Test

access(all)
struct Foo {
    access(self)
    let answer: UInt8

    init(answer: UInt8) {
        self.answer = answer
    }

    access(all)
    fun correctAnswer(_ input: UInt8): Bool {
        if self.answer != input {
            panic("wrong answer!")
        }
        return true
    }
}

access(all)
fun testExample() {
    let foo = Foo(answer: 42)

    Test.expectFailure(fun(): Void {
        foo.correctAnswer(43)
    }, errorMessageSubstring: "wrong answer!")
}
```

<h1 id="matchers">Matchers</h1>
Matchersとは、テスト関数と関連ユーティリティ機能から構成されるオブジェクトです。

```ruby
access(all)
struct Matcher {

    access(all)
    let test: fun(AnyStruct): Bool

    access(all)
    init(test: fun(AnyStruct): Bool) {
        self.test = test
    }

    /** Combine this matcher with the given matcher.
     *  Returns a new matcher that succeeds if this and the given matcher succeed.
     */
    access(all)
    fun and(_ other: Matcher): Matcher {
        return Matcher(test: fun (value: AnyStruct): Bool {
            return self.test(value) && other.test(value)
        })
    }

    /** Combine this matcher with the given matcher.
     *  Returns a new matcher that succeeds if this or the given matcher succeeds.
     */
    access(all)
    fun or(_ other: Matcher): Matcher {
        return Matcher(test: fun (value: AnyStruct): Bool {
            return self.test(value) || other.test(value)
        })
    }
}
```

`test`関数は値の評価基準を定義し、その値が関数で定義されたテスト基準に適合しているかどうかを示すブール値を返します。

`and`および`or`関数を使用して、このマッチャーを別のマッチャーと組み合わせて、複数のテスト基準を持つ新しいマッチャーを作成することができます。`and`メソッドは、このマッチャーと指定されたマッチャーの両方が成功した場合に成功する新しいマッチャーを返します。`or`メソッドは、このマッチャーまたは指定されたマッチャーの少なくとも一方が成功した場合に成功する新しいマッチャーを返します。

汎用的な型を持つテスト関数を受け入れるマッチャーは、`newMatcher`関数を使用して作成できます。

```ruby
view fun newMatcher<T: AnyStruct>(_ test: fun(T): Bool): Test.Matcher
```
型パラメータ`T`は`AnyStruct`型にバインドされます。これもオプションです。

例えば、与えられた整数値が負であるかどうかをチェックするマッチャーは、以下のように定義できます。
```ruby
import Test

access(all)
fun testExample() {
    let isNegative = Test.newMatcher(fun (_ value: Int): Bool {
        return value < 0
    })

    Test.expect(-15, isNegative)
    /* Alternatively, we can use `Test.assert` and the matcher's `test` function. */
    Test.assert(isNegative.test(-15), message: "number is not negative")
}

access(all)
fun testCustomMatcherUntyped() {
    let matcher = Test.newMatcher(fun (_ value: AnyStruct): Bool {
        if !value.getType().isSubtype(of: Type<Int>()) {
            return false
        }

        return (value as! Int) > 5
    })

    Test.expect(8, matcher)
}

access(all)
fun testCustomMatcherTyped() {
    let matcher = Test.newMatcher<Int>(fun (_ value: Int): Bool {
        return value == 7
    })

    Test.expect(7, matcher)
}
```
`Test`コントラクトには、利便性を考慮して、いくつかの組み込みのmatcher関数が用意されています。

<h3>Test.equal</h3>

```ruby
view fun equal(_ value: AnyStruct): Matcher
```
`equal`関数は、テストされた値が与えられた値と等しい場合に、成功するマッチャーを返します。`AnyStruct`値を受け入れます。
```ruby
import Test

access(all)
fun testExample() {
    let array = [1, 2, 3]

    Test.expect([1, 2, 3], Test.equal(array))
}
```

（他にもあります。詳しくは翻訳元の原文へ）

翻訳元

https://cadence-lang.org/docs/testing-framework

***
<b>[Previous << Measuring Time In Cadence](https://qiita.com/flowcadence/items/1aefd14bce23a00518ec)
</b>

[Flow blockchain / Cadence version1.0ドキュメント](https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a) (Testing)

<b>[Next >> Flow CLI](https://qiita.com/flowcadence/items/5ead732fad703556487d)
</b>