LoginSignup
0
1

More than 3 years have passed since last update.

いきなり!TDD(当方テスト初心者でいきなりTDD!) 2日目

Posted at

はじめに

前回は、TDDの概要と、簡単なinitをサンプルに、

レッド(テストを書く) > グリーン (プロダクトコードを書く) > リファクタリング(コードを整理する)

まで行いました。引き続き、TDDを行なっていきます。

開発環境

  • Xcode 10.3
  • Swift
  • XCTest

作るもの

ポーカー

現在のTODOリスト

[TODOリスト]

  • Cardを定義して、インスタンスを作成する
    • CardはSuitを持つ
    • CardはRankを持つ
  • Cardのインスタンスから文字列表記(notation)を取得する

それでは、最下段のTODOから再開していきましょう。

実装

まずは、レッドからです。TDDPokerBySwiftTests.swiftに以下を追加します。

TDDPokerBySwiftTests.swift
class TDDPokerBySwiftTests: XCTestCase {

   (省略)

   func testCardNotation() {
      let card = Card(suit: .heart, rank: .three)
      XCTAssertEqual(card.notation, "3♥")
   }
}

Command + U でテストを実行してみましょう。
スクリーンショット 2019-08-08 18.54.50.png

Cardにnotationプロパティが存在していないため、当然エラーになります。これはテストエラーではなく、単純にコンパイルエラーです。

それでは、早速、Cardにnotationプロパティを追加して、テストをグリーンにしていきましょう。

仮実装

Card.swiftに以下を追加します。

Card.swift
struct Card {

   (省略)

    var notation: String {
        return "3♥"
    }
}

Command + U でテストを実行してみましょう。
テストが成功すると思います。このnotationプロパティは、initで代入されるrankとsuitから生成されるものなので、外から代入された値を保持するStored Propertyではなく、Computed Propertyによる実装になっています。

Stored Property (保持型プロパティ)

今回で言う所の、Cardクラスのsuitとrankがそうですね。willSet、didSetなどで監視もでき、lazy修飾詞も使用可能です。

Card.swift
let suit: Suit
let rank: Rank

Computed Property (計算型プロパティ)

それ自体は値を保持せず、setter/getterで値を計算し(set)または返す(get)プロパティです。上記notationの定義がそれになるのですが、上記は、getのみのreadonly定義になっており、get{}が省略されています。省略しない場合には、以下のようになります。

Card.swift
var notation: String {
    get {
        return "3♥"
    }
}

setterを記述する場合には、

var notation: String {
    get {
        return "3♥"
    }

    set(t) {
        print("セット!\(t)");
    }
}

や、

var notation: String {
    get {
        return "3♥"
    }

    set {
        print("セット!\(newValue)");
    }
}

で、notationの値がセットされた時に、setが呼ばれます。set{}内では、Stored Propertyを含めた計算なども可能です。

具体的な使い方の例です。

class Person {
    var birthday: NSDate    // 生年月日
    var age: Int {          // 年齢
        get {
            let components = self.calendar.components(.CalendarUnitYear, fromDate: birthday, toDate: NSDate.date(), options: .allZeros)
            return components.year
        }
        set(newAge) {
            let diff = self.age - newAge
            if diff != 0 {
                self.birthday = self.calendar.dateByAddingUnit(.CalendarUnitYear, value: diff, toDate: self.birthday, options: .allZeros)
            }
        }
    }
    let dateFormatter: NSDateFormatter  // 日付フォーマッタ
    let calendar: NSCalendar            // カレンダー
    // イニシャライザ
    init(birthday: String) {
        self.dateFormatter = NSDateFormatter();
        self.dateFormatter.dateFormat = "Y/M/d"
        self.calendar = NSCalendar.currentCalendar()
        self.birthday = self.dateFormatter.dateFromString(birthday)!
    }
}

let p = Person(birthday: "1980/12/31")
print(p.age)
p.age = p.age - 2
print(p.birthday)     // 1982/12/31

三角測量

話を戻します。現在、notationは"3♥"を静的に返しているだけなので、一般化が保証されてません。テストを修正して、別のケースも追加します。

TDDPokerBySwiftTests.swift
class TDDPokerBySwiftTests: XCTestCase {

    (省略)

    func testCardNotation() {
        let card1 = Card(suit: .heart, rank: .three)
        XCTAssertEqual(card1.notation, "3♥")

        let card2 = Card(suit: .spade, rank: .jack)
        XCTAssertEqual(card2.notation, "J♠")
    }
}

このように2つ以上のテストケースを用意することで、機能の一般化を導き出すテクニックを「三角測量」と言います。テストがグリーンになるように、Card.swiftを書き換えます。enumをStringで定義し直し、各caseに文字列を代入します。取り出す際には、rawValueで取り出せます。

Card.swift
struct Card {

    enum Suit: String {
        case spade = "♠"
        case heart = "♥"
        case club = "♣"
        case diamond = "♦"
    }

    enum Rank: String {
        case ace    = "A"
        case two    = "2"
        case three  = "3"
        case four   = "4"
        case five   = "5"
        case six    = "6"
        case seven  = "7"
        case eight  = "8"
        case nine   = "9"
        case ten    = "10"
        case jack   = "J"
        case queen  = "Q"
        case king   = "K"
    }

    enum CardType {
        case other
    }

    let suit: Suit
    let rank: Rank

    var notation: String {
        return rank.rawValue + suit.rawValue
    }
}

Command + U でテストを実行しましょう。テストが成功すれば、グリーンのステップを終えたことになるので、リファクタリングを行います。今回も、cardの変数をテストケースごとに初期化して、共通利用できるようにするだけです。

TDDPokerBySwiftTests.swift
class TDDPokerBySwiftTests: XCTestCase {

    (省略)

    func testCardNotation() {
        var card: Card

        card = Card(suit: .heart, rank: .three)
        XCTAssertEqual(card.notation, "3♥")

        card = Card(suit: .spade, rank: .jack)
        XCTAssertEqual(card.notation, "J♠")
    }
}

まとめ

今回は、

  • 仮実装
  • 三角測量

について学びました。
次回は、「明白な実装」というものについて、学んで行きたいと思います。

参考

プロパティ
https://tea-leaves.jp/swift/content/%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1