コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

テストダブルの種類をざっくりまとめる

自身の記憶整理の文脈でまとめる

出典

テストダブルについては書籍 xUnit Test Patterns にて取り上げられたテストに依存するコンポーネントを置き換えるためのパターンのこと。

Test Double at XUnitPatterns.com

なおダブルは2倍のほうの意味ではなく、「代役」や「替え玉」などの意味。

テストダブルのパターン

  • TestStub
  • TestSpy
  • MockObject
  • FakeObject

TestStub(テストスタブ)

テストスタブは「テスト対象の中で行われる外部要因の戻り値を偽装する」というような用途の内容になる。

クラス外のメソッドを実行してデータを行うような処理をもつクラスがある場合、その中のメソッドの戻り値をテストコード内を定義して、期待した動きを見るなどになる。

実際あるケースとしては「色々なAPIを叩いてデータを取得した結果を総合計する」等の場合にAPIからの戻り地をテストコードで定義して期待する結果を確認するなどのコードになると思われる。

TestSpy(テストスパイ)

テストスパイは「テスト対象の中で外部への実行の実行先をすり替えて、その実行メソッドの値が期待したものが返ってくるか」というような用途の内容になる。

実際にあるケースとしては「内容を集計して、ファイル書き出しなどをする」というような場合にファイル書き出しの部分をスパイとして置き換えて、実際に書き出される期待値がマッチするかを確かめる、というようなものになると思われる。

MockObject

モックオブジェクトは「予め期待値を設定し、テスト対象の中の外部への実行を見て、期待値担っているかを判断する」というような用途の内容になる。

テストスパイと似たような動きですが、違いとしてはテストスパイはテストの実行後に値を評価するのですが、モックは予め期待値をセットするので、その期待値どおりに動くかを見るという点に違いがあります。

FakeObject

フェイクオブジェクトはまま文字通り、「偽物」として本物と同様の動きをするためのものです。他のものとは異なり、あくまでも検証のための代用品であり、このオブジェクトそのものはテストをすることに使われるものではない。

関連リンク

CSSのロードが遅くてHTMLがちらつくFOUC(Flash of unstyled content)についてざっくりまとめる

日本語情報が無いのでざっくり書いてみる

出典元

Flash of unstyled content - Wikipedia

上記のWikipedia曰く

CSS @importルールを使用している一部のページでは、MS Internet ExplorerWindows版で、スタイル化されていないページのコンテンツが一瞬フラッシュするという奇妙な表示の奇妙な現象が発生します。私はこの現象を「スタイル化されていないコンテンツのフラッシュ(Flash of Unstyled Content)」、略して「FOUC」と呼んでいます。 - Flash of Unstyled Content (FOUC)

もともとはIEで発生する現象が期限で、そこからスタイルの影響のコンテンツのちらつきに関して「FOUC」と呼ぶようになった様子。

FOUCが起きるとき

FOUCの原因はケースバイケース、HTMLの記述の問題だったり、ブラウザの機能のせいだっったり、つかっているJSのライブラリのせいだったりさまざま。

確認ポイントとしては以下のようなものがある

関連リンク

伏せ字表現にはxxxではなく、アスタリスク(*)を使うとよさそう

xxxはスラングとしての意味合いが強いので他の用語を使おうという話

今回考えるケース

個人情報保護の観点で文字列を書き換える場合の話

例えば suzuki.hideo@example.com のようなメアドは個人情報になってしまうので、差し障りのないデータとして扱うときにドメイン部をどう表現するかというような話。

候補としては

  • xxx@example.com
  • ***@example.com

などがある。

TL;DR

  • xxxなどが使われることがあるが、これはキスを意味するスラングの地域もあるので避けるほうがいい
  • パスワードでも使われる * が万能
  • foobar などのメタ構文変数で表現するのも場合によってはあり

xxxは使わないほうがいい

おそらく「ぺけ」を表すバツマークからの発展でxを使うパターンが見られるが、xxxは英語圏スラングとしてあるので、意味としては不適当な場合があるので避けるのが良さそう。

参考:「xxx」とはどういう意味?SNSで使える英語の略語 [トラベル英会話] All About

困ったら *

アスタリスクは伏せ字としてはメジャーなものであり、対抗馬として挙げられるものが「●」「〓」「✗」などの全角文字が多い中半角で表現できるので*がよさそう。

ただし注釈でも使われる記号のため、注釈が多いものでは取り扱いには気をつけないと混同する恐れがある。

使い所によってはfoo,barなどのメタ構文変数でも良さそう

foobar などのメタ構文変数でも「何かが入る」ということは表現できるので、そちらを使うのも有効だと考えられる。ただしメタ構文変数と通じないとミスリードを招くので、ケースバイケース。

参考リンク

GoogleのSEOを鑑みたときにJavaScriptをどう使うべきかはガイドラインがある

SEOとか考えるとレンダリングちゃんとやらないと不利じゃないですか…みたいな話に対して、現状どうなっているのか調べてみた

出典

明確にGoogleがドキュメントを用意してくれている

JavaScript SEO の基本を理解する | Google 検索セントラル | Google Developers

サマリ

出典のリンクがかなりわかりやすく書かれているのでそこを呼んでもらえれば大体わかるのだが、あえてかくと以下の通り

  1. URLをクローラーが受け取るとrobots.txtを見て判断クローリング対象かを判断(URLがクローリングして問題なければHTMLを取得してhrefなどの他リンク際のURLをキューとして入れる)
  2. ページ内のrobotsメタタグ、ヘッダ情報を見て問題なければレンダリングを開始
  3. headless ChromiumJavascriptなどをレンダリングして、そのHTMLをindexに登録する

ということでJavascriptを登録する

Googlebot目線でレンダリングされるページの見方

Googlebotの互換性があるコードかを確認する方法もガイドがある。

検索関連の JavaScript の問題を解決する  |  Google 検索セントラル  |  Google Developers

こちらに記載があるが、実際Googlebotがレンダリングをした場合のチェックとしてモバイルフレンドリーテストが案内されている。これを使うとどのページでも実際にレンダリングされる結果を確認することができる。

モバイル フレンドリー テスト - Google Search Console

JavascriptでDOMを組み上げるタイプのサイトでも読み込み後のHTMLが表示されるので、公開されているサイトの場合はこちらで確認することで「意図したHTMLがクローリングされないのではないか?」などの杞憂を減らすことができそう

関連リンク

劇場版SHIROBAKOの瀬川さんから正論を言ってしまう人との付き合いかたを考える

この記事は SHIROBAKO Advent Calendar 2020 20日目の記事です。

(リアルタイム更新失敗したのでクリスマスまでにかきあげました。。大変失礼しました。。)

adventar.org

2018年に関しては高梨太郎のダメっぷり反面教師の話をしましたが、今回は劇場版の瀬川さんから「厳しい先輩の裏側」を感じたので書いてみます。

TV版の瀬川さんから見るベテラン感

これは以前のSHIROBAKO AdventCalendarでKoniferさんがまとめていただいているのですが瀬川さんは各シーンでベテランならではの風格を見せます

参考:フリーランスアニメーター 瀬川美里 - Konifar's WIP

全体を通して言えることとすれば

  • 仕事を請け負う立場としてお茶を濁さず、しっかりとものを言う
  • プロとしてのクオリティ担保のための発言をする。

劇場版でみせた弱い部分

タイマスをベースにSIVAを作りあげることになり、タイマス製作時のスタッフが集まっていく過程で瀬川さんが遠藤さんを説得しにゲーセンに行く…が、仕事をしなくなっている遠藤さんに対して厳しい言葉をかけてしまい、口喧嘩をして別れてしまう。そして説得失敗した宮森にポロリと一言。

「私、正論ばっか言って相手を追い詰めるって注意されたのに…」

強い人も落ちこむときはある

おそらく宮森からも「瀬川さんならきっと遠藤さんを説得できると思うんです」のようにお願いされたであろうことなのに、怒らせる展開になり、TVシリーズではあまり見せなかったレベルで瀬川さんの落ち込む。

強くて、正しいことを言って、プロとしてカッコいい姿を見せてきた瀬川さんが落ち込むという展開が当たり前の話である「強い人でも落ち込むことがある」という側面を見せられた。

そこから学ぶこと

強い先輩も泣きたくなるときはある

普通の世界においても強い言葉を使ってプロジェクトを引っ張る人や、正論並べてぐうの音もでない仕事の進め方をする人もいる。

そういう人は「きっと強いひとだから」とどんなことでも耐えぬける、または強い言葉を浴びせても大丈夫、とは限らない。人知れず泣いている瞬間というのはあるはず

バチバチやりたくなっても落ち着くこと

「言葉として強い正論を言う人だから、同じぐらい強い言葉を使って返していい」みたいなことや、ややもすれば「この人は強い言葉を使う人だから人の心がわからない人なんだ」のように思う場面というのはあるかもしれない。でもそういう人こそ自分の強すぎる言葉の使い方に落ち込んでしまったり、人知れず泣いたりしていることもある。もしそういう人にであったときは強いひとは弱い面もあったりすることがあることを思い浮かべつつ落ち着いて接するといいのかもしれない、と劇場版の瀬川さんから思いをはせたのでした。

Rails6かつMySQLを使う場合に任意の順番でレコードを取得する

いろんなことの合せ技のためご利用は計画的に、という感じであるが知見まで。

環境

$ bin/rails -v
Rails 6.0.3.1

今回の想定ケース

例えばUserクラスがあり、Viewにはidが2,3,1の順で並べたいのでActiveRecordの取得自体も2,3,1の順番で取得したい場合。

User.where(id:[2,3,1])

上記のような書き方が想定できるが、その場合はただただ IN 句で絞り込むだけなので並び順は考慮されない ActiveRecord_Relation が返却されてしまう。

User.where(id:[2,3,1]).each do |u| pp u.id end
# User Load (0.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (2, 3, 1)
# 1
# 2
# 3

対応方法

MySQLのみとはなるが、以下のようなクエリを書くことで実現することができる。

User.order([Arel.sql('field(id, ?)'), [3,1,2]])
ids = [2,3,1]
User.where(id:ids).order([Arel.sql('field(id, ?)'), ids]).each do |u| pp u.id end
# User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (2, 3, 1) ORDER BY field(id, 2,3,1)
# 2
# 3
# 1

なぜ ORDER BY field(カラム名,2,3,1) のようなクエリで任意の並びになるのか

詳しい解説は下記ページに記載がある

日々の覚書: WHERE .. IN (..)のリストの順番でソートするORDER BY FIELDの仕組み

引用させていただくと以下

mysql56> SELECT * FROM t1 WHERE num in (7, 5, 3) ORDER BY FIELD(num, 7, 5, 3);

初めて見た時はファッ!? ってなったけど、クエリーをこう書き換えると、たぶんやってることが伝わる。

mysql56> SELECT *, FIELD(num, 7, 5, 3) AS sort_rank FROM t1 WHERE num in (7, 5, 3) ORDER BY sort_rank;

ORDER BY FIELDはORDER BY句のバリエーションじゃなくて、FILED関数の結果でORDER BYしている。 FIELD関数のドキュメントはこちら。第1引数に検索したい値、第2引数以降に検索元となるリストを与える感じ。 これが、numの値が(7, 5, 3)の何番目にあるかを整数で返すので、そこでソートできる。

とのことなので

  • FIELD関数を使って任意の順番とソート順番のマッピングリストを生成する
  • 上記リストを利用してorderで並べ替えを行う

という原理。

なぜ Arel.sql で囲むのか

これはこの実装がされたPRを見るところ

User.order(params["order_string"]) 

のような無邪気な実装でSQLインジェクションが実行されないように基本的には遮断するという実装となった。 その際にもし指定するSQL安全なものであれば Arel.sql で囲むことで回避ができる、という考えで Arel.sql で囲むことでの回避手段が提供された。

そのため、FIELDを使う場合はちゃんと利用する値にSQLインジェクションの危険性のないものかをしっかり確認してから実装したほうがよい。

参考リンク

Railsのbefore_actionで通常のインスタンス変数をセットするのは、本当にそれが最適解か考えてからやってほしい

before_actionでインスタンス変数を設定するとミスリードが多くなりがちなのでそれに対する話。

(記事製作時)の環境

# bin/rails -v
Rails 6.0.3.1

(前提として)before_actionとは

ActionControllerが提供するフィルタの機能の一種。

before_action は indexやshowなどのアクション定義をしているメソッドに対してそのメソッドを実施する前に実行するメソッドを設定することができる。

before_actionの利用例

Railsガイドでは以下のような記述が例として記載されている

class ApplicationController < ActionController::Base
  before_action :require_login

  private

  def require_login
    unless logged_in?
      flash[:error] = "You must be logged in to access this section"
      redirect_to new_login_url # halts request cycle
    end
  end
end

このメソッドはエラーメッセージをflashに保存し、ユーザーがログインしていない場合にはログインフォームにリダイレクトするというシンプルなものです。「before系」フィルタによってビューのレンダリングやリダイレクトが行われると、このアクションは実行されません。 - Action Controller の概要 - Railsガイド - フィルタ

例示にもある通り、アクションの処理の実施前に何かしらのフィルタリングをしたい場合などに利用することが例示として挙げられる

今回考えたいケース

今回考えたいのはControllerをScaffoldで作ったときにできる以下のような構成

class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]

  # GET /books
  # GET /books.json
  def index
    @books = Book.all
  end

  # GET /books/1
  # GET /books/1.json
  def show
  end

  # GET /books/new
  def new
    @book = Book.new
  end

  ######中略#######

  # DELETE /books/1
  # DELETE /books/1.json
  def destroy
    @book.destroy
    respond_to do |format|
      format.html { redirect_to books_url, notice: 'Book was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_book
      @book = Book.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def book_params
      params.fetch(:book, {})
    end
end

この記述では before_action にて インスタンス変数をsetしている。この点が微妙に厳しくなる場面があるので、可能であれば避けたい

before_actionでインスタンス変数のsetをすると何が問題なのか

大きく2つ問題がある

インスタンス変数の入るタイミング見えづらくなる

show などは before_actionと記述が近いのである程度見通しとして記述を把握しやすいが、destroyの記述はとおいので記述が遠くなるので、事前に行われる記述としてはキャッチがしづらくなる。

各action内で使われない値であれば問題はないのだが、だいたいはsetしたいものはactionの処理で使うようなデータ(Modelの取得、など)のことが多いので、各actionでインスタンス変数名を書きたい場合にbefore_actionで定義したメソッドの中まで見ないといけなくなる。

インスタンス変数の値の変更がわかりづらくなる

各actionでインスタンス変数を利用したい場合にどのようなデータが定義されたかなどを見る必要があるのと、action単体で見るとインスタンス変数が突然登場したりするケースがあるので、ちゃんと読み解かないとbefore_actionでセットされていることが気付けずデータの代入を読み違う可能がある。

また、インスタンス変数のため普通にprivateメソッドなどで値の変更を行っているとより内容がわからなくなる。

どう書くといいのか

ではどうやって書くといいのかというと、普通に各action内で呼んであげればいい

class BooksController < ApplicationController

  # GET /books
  # GET /books.json
  def index
    set_book
    @books = Book.all
  end

  # GET /books/1
  # GET /books/1.json
  def show
    set_book
  end
  
  # 中略
  
  private
    def set_book
      @book = Book.find(params[:id])
    end
  # 後略
end

更にいうのであればインスタンス変数のセットを各アクションでやってあげたほうが明快になる。

class BooksController < ApplicationController

  # GET /books
  # GET /books.json
  def index
    @books = Book.all
  end

  # GET /books/1
  # GET /books/1.json
  def show
    @book = load_book(params[:id])
  end
  
  # 中略
  
  private
    def load_book(id)
      Book.find(id)
    end
  # 後略
end

参考リンク