中年エンジニアの開発と生活の日々

中年エンジニアがソフトウェア開発や日々の生活で得た知見の備忘録

Chain Reaction Cycles で自転車を買ってみた

たまには趣味の話をと言うことで…

Chain Reaction Cycles でマウンテンバイクを買ってみました。

Chain Reaction Cycles (以降 CRC) は Wiggle と並ぶ自転車の海外通販の大手です。海外通販は安いけど怖いと言う方に注文から乗り出しまで、こんな感じですとざっくりと紹介させていただきます。需要あるかしら。

購入まで

Chain Reaction Cycles で購入しました。

サイト自体は日本語化されているのですが、自転車のスペックなどは所々英語のままなので、英語が苦手な人は Google 翻訳のお世話になりながら、チェックしてみましょう。

また、自転車のサイズはどれを選べば良いのかよくわからないという問題もありますが、ほとんどの自転車の購入ページにサイズチャートへのリンクがあります。

f:id:munacky:20170723142914j:plain

サイズチャートは以下のように英語のままですが、自分の身長と足の長さを元に合いそうなフレームサイズを注文してください。

f:id:munacky:20170723142951j:plain

決済はクレジットカードなので、通常のインターネット通販と大差ありません。なお、自転車を買うと、送料で15,000 円ほどかかりました。

海外通販の魅力

海外通販の魅力をどこに見いだすかは人それぞれですが、自分の場合は値段意外にも品揃えの充実です。

最近は日本でもスポーツ自転車のお店が増えていますが、ロードバイククロスバイクに品揃えが偏っていたり、自分の体に合うサイズがなかったりします。(特に、自分は身長が大きめなので、国内通販でもなかなか品物がなかったりします。)

今回はマウンテンバイクが欲しかったので、家の近所のスポーツ自転車のお店、国内通販を探して、良さそうなモデルがなかったため、CRC からの購入を決意しました。

配送と関税

CRC の配送は DHL です。CRC から出荷されるとトラッキングナンバーがメールが送られてきます。

f:id:munacky:20170723143758j:plain

赤枠のリンクをクリックすると配送状態が DHL のサイトで調べることができます。スマホからだと現在の状態しかわからず、細かい履歴がわからないので、細かく見たい人はPCを使いましょう。いま、どこにあるのか詳しく書かれています。

f:id:munacky:20170723143840j:plain

CRC のサイトには大体10~15営業日で到着と書いてありますが、自分の場合は7営業日で来ました。

自転車本体には関税はかかりませんが、消費税を支払う必要があります。DHL の場合は引き渡しの際の支払いでした。事前に用意しておきましょう。

開梱

梱包は自転車の販売用の箱に入れられてきます。七分組よりはかっちりと組まれていますが、以下のような状態でした

  • ハンドルはステムから抜かれている
  • シートポストはフレームから抜かれている
  • 前輪は外れている
  • フロントのディスクブレーキは借り止め
  • ドロッパーシートポストのレバーはハンドルから外れている(ワイヤーは本体に組み付けられている)
  • フロントフォークのロックアウト用のレバーとワイヤーは同梱しているだけ
  • ブレーキの引きしろは調整済み
  • シフトワイヤーは調整済み
  • 前後の反射板は同梱されているが組み付けされてはいない
  • ブレーキが左前

そんなわけで組み付け作業

自分はロードバイクの組み立てはできるのですが、マウンテンバイクは初めてでした。そんなわけで、苦労したのはフロントのディスクブレーキの調整とドロッパーシートポスト、フロントフォークのロックアウトレバーの取り付けでした。

フロントのディスクブレーキの本止め

写真の赤丸のネジが緩んだ状態で出荷されています。

f:id:munacky:20170723150811j:plain

おう、ディスクブレーキのセンター出しなんてやったことないよ~。

いろいろ調べた結果、同梱されている説明書にディスクブレーキの説明書に調整の方法が書いてありました。説明書凄いな。

「前輪をつけた状態で、ブレーキレバーを引いた状態で固定する」とのことで、ブレーキレバーを引いた状態で、フォークとブレーキを止めるネジをアーレンキーで締めていきます。

ドロッパーシートポストのとりつけ

最近はやりのドロッパーシートポストの取り付けです。ガスチェアと同じ仕組みで、手元のレバーでシートポストの上げ下げができます。

基本、ワイヤー式なので、シフトワイヤーやブレーキワイヤーの取り回しをやってことがあれば、対応できます。ワイヤリングはすでに行われているので、ハンドルにレバーを取り付けるだけです。右側と左側のどちらにもつけられますが、左側につけました。

f:id:munacky:20170723151121j:plain

ただ、レバーをつけるために、ブレーキレバーとグリップを外す必要がありました。これらもアーレンキーで外してからレバーを取り付けます。

フロントフォークのロックアウトレバーの取り付け

最近のマウンテンバイクはフロントフォークを手元のレバーでサスペンションロックすることができますが、レバーとワイヤーを取り付けます。

レバーはドロッパーシートポストと同じく左につけます。

f:id:munacky:20170723151722j:plain

そしてワイヤーを通してアーレンキーで締め付けます。適切な長さにカットしてエンドキャップをするあたりは通常のワイヤーの取り回しと同じです。

f:id:munacky:20170723151830j:plain

ハンドルの組み付け

ハンドルはアーレンキーを使用して普通にステムにつけるだけでした。楽勝。

ペダルの組み付け

ペダルはペダルレンチを使用して取り付けます。

f:id:munacky:20170723151636j:plain

なんだ、このペダルのデザインは…

右ブレーキレバーと左ブレーキレバーの交換

意外と知られていないことですが、海外では左側が前ブレーキ、右側が後ろブレーキになっています。今回購入したものも左前ブレーキになっていました。さて、ブレーキレバーを交換しようと思ったのですが、油圧式のため、レバーの交換にはオイルとエアー抜きが必要なため、断念しました。いまも、左前ブレーキの設定で乗っています。

組み付けに使用した工具

結局、組み付けに使用した工具は以下の通りです。

ワイヤーカッターとペンチはサスペンションロックのワイヤーを取り付けるために使用しました。

まとめ

海外通販のイメージはつかめたでしょうか? スポーツ自転車をある程度自分で整備・調整できる人なら、是非海外通販を使ってみましょう。ただし、自分の自転車は自分の責任において面倒を見る覚悟が必要です。

Appium でタッチ操作を実行するには

事の発端

スマートフォン向けアプリのテストを Turnip と RSpec で作成していて、画面をスワイプしてスクロールする必要に迫られて、ユーザーのタッチ操作をどのようにスクリプト記述するのかを調べながら実装してみました。

一般に Appium でテストスクリプトを書く場合は SeleniumAPI を使用して書くのですが、Selenium は元々 Web のテストフレームワークだけあって、タッチジェスチャーなどのスマートフォンの操作を記述することは範囲外です。そのため、Appium の API で記述することになります。

以下は、本家のリファレンスです。

上記サイトを見てもらえると、一般的なタッチジェスチャーが網羅されていることがわかります。

呼び出し方

基本的にはメソッドチェーンを使用して使用するのが良いと思います。

  • TouchAction のインスタンスを生成
  • タッチアクションのメソッドを順次に呼び出し
  • perform メソッドを呼び出して実行

個人的には最後の perform メソッドを呼び出さないとタッチ操作が発動しないというのがわからなくて、しばらく悩みました。 (ドキュメント、ちゃんと読めという話ですが…)

一番簡単なタップは以下のコードで実現できます

pointX = 100
pointY = 200

Appium::TouchAction.new.tap(x:pointX, y:pointY).perform

TouchAction.new でインスタンスを作成し、tap で座標を指定、perform でコマンドを発動させます。tap の戻り値が自分自身なため、メソッドチェーンで次々にコマンドを呼び出すことができます。

メソッドチェーンを使って、複雑な操作を実現することもできます。

pointX = 100
pointY = 200
duration = 2000

Appium::TouchAction.new.press(x: pointX, y: pointY).wait(duration).release.perform

この例では (100, 200) の座標を押下(press)して2秒長押し(wait)した後に指を離す(release)ジェスチャーになります。

画面をスクロールさせるには

swipe を使用して画面をスクロールさせます。swipeは iOSAndroid で引数が違うので使いずらいのですが、iOS では始点の座標と動かすオフセット、何秒かけて動かすかのdurationを設定します。 画面をどの程度スクロールさせるかは始点の座標とオフセットの距離で決まります。画面の論理解像度を取得して、テスト対象のアプリケーションの実装と照らし合わせて、異なる機種でも大丈夫なように座標を計算するのがよいでしょう。

以下は Scroll View の画面サイズと位置を元に下から上に、だいたい0.8画面分スクロールさせるサンプルです

scrollView = find_element(:accessibility_id, 'idOfScrollView)

startX = scrollView.location.x + scrollView.size.width/2
startY = scrollView.location.y + scrollView.size.height * 0.8
offsetY = scrollView.size.height * 0.8
    
Appium::TouchAction.new.swipe(start_x: startX, start_y: startY, offset_x: 0, offset_y: - offsetY, duration: 400).perform

ちなみに、画面のスクロールに関しては Appium には Scroll 用の API があるのですが、テストフレームワークとの相性が合ったりして安定しないようで、皆さん苦労されているようです。

Turnip の RSpec で関数を定義して呼び出すには

現在、Turnip と RSpec を組み合わせて Ruby でテストスクリプトを作成しているのですが、共通な処理を関数にまとめる方法についてまとめてみました。

Turnip については以下のサイトが詳しいです

Rubyist Magazine - エンドツーエンドテストの自動化は Cucumber から Turnip へ

Turnipでは Feature ファイルと Stepファイルに分かれていて、Step ファイルは RSpec でテストスクリプトを記述していきます。

RSpecについては以下のサイトが詳しいです relishapp.com

テストスクリプトを書いていると実感するのですが、同じ処理をパラメータを変えて繰り返すことが多いです。そのため、同じ処理を関数として定義して呼び出さないと見通しが悪いですし、メンテナンスも大変です。ところが、通常の Ruby のやり方ではうまくいきません。

たとえば、Stepファイルに以下のようにして関数を定義して、Step の中で呼びだしてみます。

  def putsHogehoge
    puts "hogehoge"
  end

step "Step1" do
  putsHogehoge
end

通常の Ruby でしたら、"Step1" の中で putsHogehoge がコールされてコンソールに “hogehgoe” と表示されますが、Turnip で Feature ファイルからこのStep1を呼び出すと以下のようなエラーメッセージが表示されます。

Failures:

  1) Test Sample Given GetParam
     Failure/Error: putHogehoge

     NameError:
       undefined local variable or method `putsHogehoge' for #<RSpec::ExampleGroups::Test::Sample:0x007fb676a58ac8>
     # spec/steps/sample_steps.rb:13:in `block in <top (required)>'
     # ./vendor/bundle/ruby/2.3.0/gems/turnip-2.1.1/lib/turnip/execute.rb:25:in `step'
     # ./spec/features/login.feature:5:in `run_step'
     # ./vendor/bundle/ruby/2.3.0/gems/turnip-2.1.1/lib/turnip/rspec.rb:44:in `instance_eval'
     # ./vendor/bundle/ruby/2.3.0/gems/turnip-2.1.1/lib/turnip/rspec.rb:44:in `run_step'
     # ./spec/features/sample.feature:6:in `block (4 levels) in run_feature'
     # ./spec/features/sample.feature:5:in `each'
     # ./spec/features/sample.feature:5:in `block (3 levels) in run_feature'
     # ./spec/features/sample.feature:5:in `Step1'

Step1 は Turnip のフレームワークから呼び出されているようで、すぐ上に定義されている putsHogehoge という関数が見えないようです。

解決策

以下のサイトを参考に Turnip で拡張されている箇所のつじつまを合わせたら、Helper Method を呼び出すことができました。 relishapp.com

  • Spec フォルダに helpers.rb を作成します。別に stepsディレクトリに作成してもよい気もしますが、Step ではなく、Helper なので一つ上のディレクトリに格納しています。
  • helpers.rb に Helpers Module を定義して、その中に関数を定義します
# coding: utf-8

module Helpers
  def putsHogehoge
    puts "hogehgoe"
  end
end
  • spec_helper.rb に require ‘./spec/helpers’ を追加します。
require 'rubygems'
require 'selenium-webdriver'
require 'appium_lib'
require 'bundler/setup'
require './spec/helpers'  # <- 追加
  • spec_helper.rb の RSpec configure に c.include Helpers を追加。これにより、Helpers Module が各 Step でロードされます。
RSpec.configure do |c|
  c.include Helpers # <- 追加

  c.before(:each) {
    @driver = Appium::Driver.new(desired_caps).start_driver
    @driver.manage.timeouts.implicit_wait = 5
    Appium.promote_appium_methods Object
  }
  c.after(:each) {
    @driver.quit
  }
end

こうすることによって、Turnip の Step ファイルから自分で定義した関数を呼び出せるようになりました。

Appium のテストスクリプトで長めの Sleep を入れる

本日は小ネタです。

内部の実装上の都合で、Appium のテストスクリプト (Rubyで書いてます) の中で長めの Wait を入れる必要がありました。テストスクリプトの中で

  sleep 90

とか、長めの Sleep を入れたところ、テストスクリプトの実行時に以下のようなエラーメッセージが出ました。

     Failure/Error: @driver.quit
     
     Selenium::WebDriver::Error::NoSuchDriverError:
       A session is either terminated or not started

なにもしてないのに、なぜ故に@driver.quit がこけるのだ。すでにセッションが終了しているだと…

エラーの原因

Appium のログを見たところ以下のような記載がありました。

[BaseDriver] Shutting down because we waited 60 seconds for a command
[Appium] Closing session, cause was 'New Command Timeout of 60 seconds expired. Try customizing the timeout using the 'newCommandTimeout' desired capability'

どうも、コマンドのタイムアウトが60秒でそれを超えるとセッションが閉じるようです。何もしていないのに @driver.quit が失敗したのではなく、何もしなかったので @driver.quit が失敗してしまったというわけです。

解決策

ログの通りに、appium 起動時の設定オプションの desired capability に newCommandTimeout=120 と設定したら、90秒の Sleep を挟んでも問題なくテストスクリプトが動作しました。

Circle CI でバックグラウンド処理を行う

CI におけるバックグラウンドプロセスの実行について

Circle CI でバックグラウンドプロセスを起動したいことがままあります。たとえば、自動検証を行う際に appium のサーバープロセスを起動するなどです。

普通、Unix系のコマンドではコマンドの後ろに ‘&’ をつけて、実行するとバックグラウンド実行になります。

> appium &

しかしながら、circle.yml にコマンドとして以下のように書くと…

test:
  pre:
    - appium&

実行時に次のような警告が出てきます。

Probable error. It looks like you’re trying to run a command in the background by using ‘&’ and not the ‘background: true’ option. If so, the process will die quickly from the hangup (HUP) signal. See the the documentation on backgrounding.

と、言うわけで、Circle CI 本家のドキュメントを見てみます。

circleci.com

このドキュメントによると、バックグラウンド処理は & を使うのではなく、yml の記法で background オプションを true にせよとのことです。また、必要に応じてバックグラウンドプロセスが安定するまでスリープを入れてもよいとのことです。

そんなわけで、以下のように circle.yml に記載することによって、appium のサーバーがバックグラウンドプロセスとして起動することができます。

test:
  pre:
    - appium:
        background: true
    - sleep 10

バックグラウンドプロセスの標準出力をみるには

Circle CI ではバックグラウンドプロセスの標準出力とエラー出力をビルド後に確認することができます。通常では Circle CI の仮想マシンのログなどを引き上げるときは artifacts としてファイルパスを circle.yml に記載する必要がありますが、バックグラウンドプロセスの標準出力とエラー出力については自動的に artifacts として待避されます。ビルド、自動テスト中にエラーが出た場合にも後からログとして確認することができます。

Circle CI の結果画面で artifacts タブを選択すると以下のような表示になります。 f:id:munacky:20170524183848p:plain

このファイル群の stdout から始まるのが、標準出力、stderr から始まるのがエラー出力になります。

自分はこの機能を知らず、わざわざ、出力先を自前でリダイレクトして、リダイレクトしたファイルを artifacts に登録して出力結果を引き上げていました。とほほ。

Ruby の空白の洗礼を受けたお話

最近、AppiumにTurnipとRSpecを組み合わせてテストスクリプトを書き始めています。

今までのエンジニア人生でRubyを本格的に開発に使用するのが初めてなので、Rubyのエキスパートから見たらどうしょうもないところでつまづいてしまいました。 自分の恥を晒すついでに、忘れないために記事にしています。

それは適当に書いた1行のコードから始まった

今回、つまづいたのは以下のコード。myArrayのサイズが4かどうかを判定するテストスクリプトのつもりで書きました

expect (myArray.size).to eq 4

実行すると以下のようなエラーメッセージが

     Failure/Error: expect (myArray.size).to eq 4

     NoMethodError:
       undefined method `to' for 4:Fixnum

Fixnumには to というメソッドはないそうですよ。

勝手な解釈で試行錯誤を続ける

「あれ、おかしいのう、もしかしてexpectに数値渡すと .to eq で評価できないのか?」

と早合点したのが運の尽き。expectの引数に色々なオブジェクトを設定したりしても解決しません。30分以上試行錯誤を繰り返してようやく

「おりょ、my.Array.size の to が expect より先に評価されてるんじゃね?」

ということに気がつきました。コードをもう一度よく見てみると…

expect (myArray.size).to eq 4

「あー、expectと括弧の間にスペースが紛れてる〜」

と、いうことに気がつきました。そんなわけで、以下のようにexpectと括弧の間のスペースを削除したら動作するようになりました。

expect(myArray.size).to eq 4

まとめ

Ruby は関数呼び出しに必要な括弧をスペースで省略できる言語のため、関数呼び出しの際のスペースが重要な意味を持ちます。 これはなるべく括弧を書きたくないという思想が生み出した言語仕様でとても気に入っています。

今まで使ってきたC++JavaJavaScriptなどの言語では関数名と括弧の間のスペースは無視されていたため、あまり気にしなかったのですが、Ruby ではキッチリと意識をして空白を使わないといけないようです。

自動署名のXcodeプロジェクトをfastlaneでビルドする

自動署名とは

iOS アプリの開発においてXcode8 から導入された機能で、アプリケーションをビルド&アーカイブする際に選択した使用方法に合わせた署名をつけてくれます。 通常、署名の種類は開発用(Development)、検証用(Adhoc)、リリース用の3種類の署名があります。

自動署名機能を使用するかどうかはXcodeプロジェクトの設定ファイル(.xcodeproj)に格納されます。

自動署名で困ること

fastlane を使用して CI 環境でビルドする際にはビルドするブランチによって、使用する署名を指定する機会が多いです(例:develop ブランチなら Adhoc 用の署名をつけて、masterブランチなら Release用の署名をつけるとか )

この時にプロジェクトの設定が自動署名になっていると fastlane を使用して署名の種類を指定してビルドするとプロジェクトの設定とfastlaneの設定が食い違い、ビルドエラーになってしまいます。

解決策

自動署名機能を使わないという極端な解決策もあるのですが、せっかくの機能を使わないと勿体無いし開発者の開発効率を CI のために落とすのも本末転倒なので、以下のように運用しています。

  • fastlaneを使用してビルドを行う場合、最初にプロジェクトの設定をマニュアル署名に設定する
  • ビルド&アーカイブ処理を実行
  • ビルド&アーカイブが終わったらプロジェクトの設定を自動署名に戻す

xcodeproj ファイルは XML ファイルなので頑張れば自分で解釈して書き換えも頑張ればできるのですが、fastlaneにはプロジェクトの自動署名設定を書き換えるためのコマンドがあります。disable_automatic_code_signing と enable_automatic_code_signing です。引数にxcodeprojファイルのパスを指定して設定を変更するプロジェクトを指定します。

例えば、検証用(AdHoc)の ipa ファイルを作成する場合は以下のようになります。 xcargs の引数は各自の署名を識別できる正しい値を使用してください

  lane :beta do
    #真っ先にプロジェクトの自動署名をオフにする
    disable_automatic_code_signing(path: "XXXX.xcodeproj")
    sigh(adhoc: true)
    gym(
      clean: true, 
      workspace: 'XXXX.xcworkspace', 
      scheme: 'XXXXXX',
      configuration: 'Release',
      xcargs: "CODE_SIGN_IDENTITY='XXXX' PROVISIONING_PROFILE_SPECIFIER='XXXXXXXXXXXX'"
      ) 
    #ビルドが終わったら自動署名を元に戻す
    enable_automatic_code_signing(path: "MyApp.xcodeproj")
  end

おまけ

本来なら fastlane 側でもう少し柔軟に対応できないかとも思うのですが、今の所はこの方法で乗り切るしか無いようです。