ためすう

Ruby でインターフェースクラスを作る

2019-06-10

やったこと

Ruby では下記のようなインターフェースがありません。

PHP: オブジェクト インターフェイス - Manual

そこで、Ruby でも似たようなことを実現する方法を試してみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

調査

継承で実現する

main.rb

class MyInterface
  def hoge
    raise NotImplementedError.new("#{self.class}##{__method__} を実装してください")
  end
end

class SampleFailed < MyInterface
end

class SampleSuccess < MyInterface
  def hoge
    p "#{self.class}##{__method__}が実装されている!"
  end
end

SampleSuccess.new.hoge
SampleFailed.new.hoge

出力結果

$ ruby main.rb
"SampleSuccess#hogeが実装されている!"
Traceback (most recent call last):
	1: from main.rb:17:in `<main>'
main.rb:3:in `hoge': SampleFailed#hoge を実装してください (NotImplementedError)

Module で実現する

main2.rb

module MyInterface
  def hoge
    raise NotImplementedError.new("#{self.class}##{__method__} を実装してください")
  end
end

class SampleFailed
  include MyInterface
end

class SampleSuccess
  include MyInterface

  def hoge
    p "#{self.class}##{__method__}が実装されている!"
  end
end

SampleSuccess.new.hoge
SampleFailed.new.hoge

出力結果

$ ruby main2.rb
"SampleSuccess#hogeが実装されている!"
Traceback (most recent call last):
	1: from main2.rb:20:in `<main>'
main2.rb:3:in `hoge': SampleFailed#hoge を実装してください (NotImplementedError)

参考

Rails5 で System Spec を使ってみる

2019-06-10

やったこと

System Spec を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

$ gem list | grep rspec-rails
rspec-rails (3.8.2)

調査

System Spec とは

Rails の SystemTestCase を Rspec から利用できるようにしたもの

SystemTestCase は E2E テストを実行することができます。

使ってみる

ファイル準備

$ mkdir spec/system
$ touch spec/system/tasks_spec.rb

app/views/tasks/new.html.erb

<button onclick="if (confirm('ok???')) { alert('pushed ok!!!'); }">
  テストボタン
</button>

spec/system/tasks_spec.rb

require 'rails_helper'

RSpec.describe 'Tasks', type: :system do
  it 'js test' do
    visit '/tasks/new'
    click_button('テストボタン')
    expect(page.driver.browser.switch_to.alert.text).to eq 'ok???'
    page.accept_confirm
    expect(page.driver.browser.switch_to.alert.text).to eq 'pushed ok!!!'

    page.accept_alert
  end
end

System Spec を実行

$ rails spec spec/system/tasks_spec.rb

参考

JavaScript の encodeURI と encodeURIComponet を使ってみる

2019-06-09

やったこと

JavaScript の encodeURI と encodeURIComponet を使ってみます。

確認環境

navigator.userAgent
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"

調査

encodeURI を使う

encodeURI("/hogehoge/index?a=22&b=ああ")
"/hogehoge/index?a=22&b=%E3%81%82%E3%81%82"

エスケープされない文字: A-Z a-z 0-9 ; , / ? : @ & = + $ - _ . ! ~ * ‘ ( ) #

encodeURIComponent を使う

encodeURIComponent("/hogehoge/index?a=22&b=ああ")
"%2Fhogehoge%2Findex%3Fa%3D22%26b%3D%E3%81%82%E3%81%82"

encodeURIComponent は次を除く全ての文字をエスケープします : アルファベット、10進数字、- _ . ! ~ * ‘ ( )

encodeURIComponent は、URI の構成要素となる文字列 をエスケープするものです。

参考

jq を使って json を整形してみる

2019-06-09

やったこと

jq コマンドを使って、json を見やすいように整形してみます。

確認環境

Mac High Sierra (10.13.6)

調査

jq のインストール

$ brew install jq

json を出力する

jq なし

$ echo '{"root":[{"group1": [1]},{"group2": [3,4,5]}]}'
{"root":[{"group1": [1]},{"group2": [3,4,5]}]}

読みにくいですね。

jq あり

$ echo '{"root":[{"group1": [1]},{"group2": [3,4,5]}]}' | jq
{
  "root": [
    {
      "group1": [
        1
      ]
    },
    {
      "group2": [
        3,
        4,
        5
      ]
    }
  ]
}

階層が分かりやすくなりました。

ちなみに json が壊れてたりすると、下記のようなエラーが出ます。

$ echo '{"root"' | jq
parse error: Unfinished JSON term at EOF at line 2, column 0

参考

アジャイルソフトウェア開発の奥義 第4章 テスティング

2019-06-08

「アジャイルソフトウェア開発の奥義」の読書メモです。

時々、個人の見解入りです。

ユニットテストを書くという行為は、機能検証というよりも設計に近い行為である。また、ドキュメント作成に近い行為であるとも言える。

テストを最初に書くという行為は、設計上の判断をふるいにかける行為でもある。

テスティングフレームワークに頼らない場合、本章にある通り、設計を気にする必要がある。

Ruby on Rails などの場合、テスティングフレームワーク の機能がリッチだと思うので

テスト実行時に、テスト対象の処理を上書きする形にすればテストは実現できそうである。

Mock Object パターンも、テスト専用の処理をどこに、どうやって書くかの1つの選択肢。

テスティングフレームワークに頼りすぎると、基盤の移行をする際、選択肢が少なくなる可能性があるのがデメリットと考える。

根底にある考えは理解する必要があると思うが、現実に即して考えると全部真似する必要はないと思う。

Active Support の concern を使ってみる

2019-06-08

やったこと

Active Support の concern を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

調査

concern を使う主な理由

mix-in で同じことをやると記述が複雑になりますが、concern を使えば簡潔に書くことができます。

また、複雑な依存関係を考慮しなくて良くなります。

確認 (concern なし)

test.rb

module Hoge2
  def self.included(base)
    base.class_eval do
      def self.method_injected_by_hoge2
        'method_injected_by_hoge2'
      end
    end
  end
end

module Hoge1
  def self.included(base)
    base.method_injected_by_hoge2
  end
end


class Sample
  include Hoge2
  include Hoge1
end

p Sample.method_injected_by_hoge2

ここで、include Hoge2 がないと下記のエラーで落ちます。

Traceback (most recent call last):
	3: from ruby-mix-in.rb:18:in `<main>'
	2: from ruby-mix-in.rb:19:in `<class:Sample>'
	1: from ruby-mix-in.rb:19:in `include'
test.rb:13:in `included': undefined method `method_injected_by_hoge2' for Sample:Class (NoMethodError)

確認 (concern あり)

test.rb

require 'active_support/concern'

module Hoge2
  extend ActiveSupport::Concern

  included do
    def self.method_injected_by_hoge2
      'method_injected_by_hoge2'
    end
  end
end

module Hoge1
  extend ActiveSupport::Concern

  include Hoge2

  included do
    self.method_injected_by_hoge2
  end
end


class Sample
  include Hoge1
end

p Sample.method_injected_by_hoge2

Sample クラスは Hoge1 だけ include すれば良くなりました。

参考

Selenium [DEPRECATION] Selenium::WebDriver::Chrome#driver_path= is deprecated が出る件

2019-06-08

やったこと

Selenium に関する警告が出るようになったので修正します。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

調査

メッセージ

2019-06-08 14:23:23 WARN Selenium [DEPRECATION] Selenium::WebDriver::Chrome#driver_path= is deprecated. Use Selenium::WebDriver::Chrome::Service#driver_path= instead.

書いてある通りですが、

[非推奨] Selenium::WebDriver::Chrome#driver_path=

[推奨] Selenium::WebDriver::Chrome::Service#driver_path

みたいです。

修正

Today I got the same issue and the reason of that was a gem called chromedriver-helper in Gemfile, I just removed this gem and after I ran this command: bundle install

参考リンクを見ると、chromedriver-helper を削除したら、解決できそうです。

また、

If your code is using selenium, perhaps with the Watir gem, use the Webdriver gem instead of chromedriver-helper now. gem ‘webdrivers’, ‘~> 3.0’

chromedriver-helper の代わりに、webdrivers を使うと幸せになれるかもしれない。

とあるのでやってみます。

Gemfile

-  gem 'chromedriver-helper'
+  gem 'webdrivers', '~> 3.0'
$ bundle install

これで警告が出なくなりました。

参考

hashdiff に関する警告が出たので修正する

2019-06-08

やったこと

WebMock をインストールしたら、hashdiff に関する警告が出るようになったので修正します。

エラーで止まるわけではないので、ワーニングのようです。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

調査

メッセージ

The HashDiff constant used by this gem conflicts with another gem of a similar name.  As of version 1.0 the HashDiff constant will be completely removed and replaced by Hashdiff.  For more information see https://github.com/liufengyun/hashdiff/issues/45
  • 定数で似ているものが使われている
  • HashDiff -> Hashdiff に置き換えられる

修正

メッセージのリンクを辿ると、下記に解決策がありました。

Conflicts with hash_diff · Issue #45 · liufengyun/hashdiff

Gemfile

gem 'hashdiff', ['>= 1.0.0.beta1', '< 2.0.0']

$ bundle install
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Fetching gem metadata from https://rubygems.org/.........
Fetching gem metadata from https://rubygems.org/.
You have requested:
  hashdiff >= 1.0.0.beta1, < 2.0.0

The bundle currently has hashdiff locked at 0.4.0.
Try running `bundle update hashdiff`

If you are updating multiple gems in your Gemfile at once,
try passing them all to `bundle update`

update してみます

$ bundle update

(これで該当の警告は出なくなりましたが、新しい警告が出るようになりました。)

参考

rspec で WebMock を使う

2019-06-08

やったこと

WebMock の stub を利用して rspec でテストを書いてみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

$ gem list | grep rspec-rails
rspec-rails (3.8.2)

調査

インストール

Gemfile

group :development, :test do
  gem 'webmock'
end
$ bundle install

spec/models/task5_spec.rb

require 'rails_helper'
require 'webmock/rspec'

RSpec.describe Task, type: :model do

  before do
    WebMock.enable!
  end

  it 'test1' do
    WebMock.stub_request(:get, "http://localhost").to_return(
      body: JSON.generate({b: 3, c: 8}),
      status: 200
    )

    p Net::HTTP.get('localhost', '/')
  end
end

出力結果

$ bundle exec rspec spec/models/task5_spec.rb
"{\"b\":3,\"c\":8}"
.

Finished in 0.0048 seconds (files took 1.5 seconds to load)
1 example, 0 failures

参考

rspec で Faraday の stub を使う

2019-06-07

やったこと

Faraday の stub を利用して rspec でテストを書いてみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

$ gem list | grep rspec-rails
rspec-rails (3.8.2)

$ gem list | grep faraday
faraday (0.15.4)

調査

spec/models/task4_spec.rb

require 'rails_helper'

RSpec.describe Task, type: :model do

  it 'test1 allow success' do
    conn = Faraday.new do |conn|
      conn.adapter :test, Faraday::Adapter::Test::Stubs.new do |stub|
        stub.get '/fuga/test.json' do
          [200, {}, JSON.generate({a: 'hogehoge'})]
        end
      end
    end

    response = conn.get '/fuga/test.json'
    p response.body
  end
end

出力結果

$ bundle exec rspec spec/models/task4_spec.rb
"{\"a\":\"hogehoge\"}"
.

Finished in 0.00817 seconds (files took 2.74 seconds to load)
1 example, 0 failures

所感

こんな感じでレスポンスを差し替えることができます。

メリット

外部サービスのAPIのテストなどがやりやすくなりそうです。

デメリット

外部サービスのAPIのレスポンスが変わってもテストでは気づきにくくなりそうです。

レスポンスが変わったことについて、テストを実行しないと気付かないという体制の方が問題だと思うので、デメリットはあまり気にしなくても良いかもしれません。

参考