Webアプリつくってみよう 4日目 / 全5回


こんにちは、Gaji-Labo エンジニアの山崎です。
この記事は Gaji-Labo AdventCalendar 2015 17日目の記事です。

前回はUI側の実装をご紹介しました。
JavaScriptについて詳しくというよりは、便利なライブラリのご紹介となってしまいましたが…。
今回は2日目に作成したAPIと3日目に作成したUI側の実装を結合させていきます。

結合テスト

大きなプロジェクトであれば、色々と確認する内容があり手法も様々あるかと思います。
今回は単純なAPIへアクセスするだけですので大掛かりなことはありません。

jquery-mockjax の設定を解除します。
app/assets/javascripts/mockjax.js.coffeeの該当行をコメントアウトすればOKです。

再度フォームを操作してみると、ちゃんと入力した郵便番号の住所が取得されました。
これで結合テストは完了し、要件を満たしたアプリケーションが完成しました。

追加要件による仕様変更

プロジェクトが進行していくにつれ、様々な要件が追加されていきます。
中には大きく仕様に関わる内容もあるでしょう。
そういった場合、テストを書いておくことで最低限の労力で検証を行うことができます。

今回は、「郵便番号の入力途中で候補をサジェストしたい。」という追加要件が発生した想定で進めていきます。
3桁で検索を行うとかなりの件数が出てきますので、「4桁まで入力した場合にサジェストの候補を取得する。」という仕様とします。

API側の実装

既存のメソッドを拡張して仕様変更に対応していきます。
テストを書いておくことにより既存の機能への影響を恐れずに追加実装やリファクタリングが容易になります。

では、まずはテストを書いていきます。

spec/controllers/postcode_controller_spec.rb

  describe "GET #search" do
.....

it "postcode matcher" do
get :search, postcode: "103-0"
json = JSON.parse(response.body)
expect(json.size).to match(23) # ヒットするであろう件数
end
end

本来であれば、中身を全てチェックするテストが望ましいですが、
今回は省略し動作していることをテストします。

もちろんこの段階ではテストに失敗しますのでモデルに実装を行います。
※ 3文字以下のリクエストを受けた場合はメッセージも返さないように仕様を変更しています。

app/models/postcode.rb


class Postcode < ActiveRecord::Base

def self.search(query) query = query.gsub(/-|ー/, "") query.tr!("0-9", "0-9") return if query.length <= 3<="" p="">

if query.length == 7 postcode = self.where(postcode: query).first else postcode = self.where("postcode like ?", "#{query}%") end

if postcode.present? return postcode else return { message: "not found." } end end end

既存のメソッドをほぼ作り直しをしている状態になりました。
テストが無ければ、今までの機能を再度手動でチェックする必要がありますね。

しかし、すでに実装済みの機能についてはテストがありますのでコマンドを実行するだけです。


$ bundle exec rspec
....

Finished in 0.293 seconds (files took 2.12 seconds to load) 4 examples, 0 failures

完璧です。テストが通りましたね。

UI側の実装

さて、API側の実装が進んでいる間にUI側の実装も平行して進めましょう。
jquery-mockjax がありますのでAPI側の実装完成を待つ必要はありません。

app/assets/javascripts/mockjax.js.coffeeを以下のように修正します。


$.mockjax
  url: "/search/*"
  response: (settings) ->
    postcode = _.last settings.url.split("/")
    if /^d{3}-d{4}$/.test(postcode) or /^d{7}$/.test(postcode)
      this.responseText = {
        "prefecture": "東京都"
        "city": "渋谷区"
        "address": "千駄ヶ谷"
      }
    else if /^d{3}-d{1,4}$/.test(postcode) or /^d{4,7}$/.test(postcode)
      this.responseText = [
        {
          "postcode": "1510051"
          "prefecture": "東京都"
          "city": "渋谷区"
          "address": "千駄ヶ谷"
        }
        , {
          "postcode": "1510051"
          "prefecture": "東京都"
          "city": "渋谷区"
          "address": "千駄ヶ谷"
        }
        , {
          "postcode": "1510051"
          "prefecture": "東京都"
          "city": "渋谷区"
          "address": "千駄ヶ谷"
        }
      ]
    else
      this.responseText = { "message": "not found."}

これで、APIの振る舞いをモックで再現できましたのでJavaScript本体の実装を進めていきます。

入力途中でAPIへのアクセスが必要となりましたので、
サブミットボタンは削除し、keyup イベント毎にAPIへアクセスするように実装します。

app/assets/javascripts/postcode.js.coffee


$ ->
  PostcodeView = Backbone.View.extend
    el: 'body'

events: "keyup #postcode": "onKeyUp"

onKeyUp: -> postcode = $("#postcode").val() if postcode.length > 3 $.getJSON "/search/#{postcode}", (data) -> $address = $(".address") if data if data.message $address.html data.message else if data instanceof Array $address.empty() _.each data, (d) => $address.append """ #{d.postcode} #{d.prefecture} #{d.city} #{d.address}
""" else $address.html """ #{data.prefecture} #{data.city} #{data.address} """

new PostcodeView()

実際にフォームを操作してみると、jquery-mockjax に設定したテストデータが取得できました。

結合テスト

さて、API・UI共に実装が完成したところで冒頭と同じく結合テストを行います。
要件通り4桁以上の郵便番号を入力した際に郵便番号と住所のリストが表示されるようになりました。

4日目まとめ

今回は、今までより現場に近い実装の動きをご紹介しました。

初めに実装していく段階ではテストを用意することが負担になるケースもあると思いますが、
仕様変更などアプリケーションに手を加えていくにつれテストの重要性が高まります。

また、テストがあることにより新しい人が参加する際に、
アプリケーションへの理解と作業の着手がスムーズになるでしょう。

関連リンク


投稿者 Gaji-Labo Staff

Gaji-Laboの社内デジタル環境でいろいろなお手伝いをしているがじ専務&じら常務。みんなのシリーズ記事をまとめたり、卒業したスタッフの過去記事を記録したり、Twitterをやったりしています。