Productivity Tips フィヨルドブートキャンプ

【Learning Diary1】依存性注入/Rubygemのテストとbuild

依存性注入RubyGem/Rspec/webmockraike build Learning Diary1

UnsplashDavid Travisが撮影した写真

日々学んだことを記録していきます。

依存性注入(Dependency Injection: DI)

rubygemのテストに取り組んでいることをメンターさんに報告した際、
テストもレイヤーごとに選択し行うことを教えてもらう。

その中に出てきた一つが、
「依存性注入(Dependency Injection: DI)」。

プログラムが他のプログラムに依存している状態を依存性という。

注入とは、外部からのアクセスを許可するインターフェースを渡すイメージかな。

つまり、クラスとインスタンスなど
オブジェクト間の依存関係を整理し、
責務を分離させ、保守性を高める手法と表現できそう。

 

依存性注入(Dependency Injection: DI)について理解する

RubyGemのAPIリクエストのテストを書いてみる

 

今回はを使ってテストしたい。

今回対処が必要な箇所は、「当日の気象情報」であるところ。
アクセスする日によって異なるデータが返却される。

locationで固定され日時の影響を受けない経度と緯度の部分のみを読み込み確認したい。

to_return のbodyについて、希望にそう記述ができないか調べる。

 

使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」

 

今回 参考になりそうな記事を発見。

 

使えるRSpec入門・その3「ゼロからわかるモック(mock)を使ったテストの書き方」

 

モックについての理解は深まったけれど、知りたい部分の解消には至らず。

記事を読みながら、理解があやふやになって来たのに気づいたので確認。

 

スタブ

メソッド内のコードを外形的に呼び出し、テストでほしい値を返却させること。
(実際に呼び出しはかかっていない。)

 

Rails tips: RSpecでシンプルなスタブを使う(翻訳)

 

確認したところで読み進める。

 

webmock使ってみた

 

gem webmock の README を翻訳しました

 

出力結果に緯度・経度はふくまれない点に思いいいたり、検証データを設定することに。

require "weather_today"
require "webmock/rspec"
require "spec_helper"

RSpec.describe WeatherToday::WeatherClient do
  it "has a version number" do
    expect(WeatherToday::VERSION).not_to be nil
  end

  context "when fetching weather data for kagoshima" do
    before do
      stub_request(:get, /api\.open-meteo\.com/).to_return( 
        status: 200, 
        body: { 
          "daily" => { 
            "time" => ["2023-09-27"], 
            "weathercode" => [1], 
            "temperature_2m_max" => [30], 
            "temperature_2m_min" => [20], 
            "sunrise" => ["06:00"], 
            "sunset" => ["18:00"], 
            "uv_index_max" => [5] 
          }
        }.to_json
       )
    end

    it "returns weather data with correct latitude and longitude" do
      # テスト対象のWeatherClientを作成
      weather_client = WeatherToday::WeatherClient.new("kagoshima")       # テスト実行
      weather_data = weather_client.fetch_weather_data       # HTTPステータスコードが成功 (200) であることを確認
      expect(weather_data).to be_a(Hash) # weather_dataがハッシュであることを確認

      # レスポンスの中身を詳細に検証する
      expect(weather_data).to have_key("UV指数") 
      expect(weather_data).to have_key("天気")
      expect(weather_data).to have_key("日の入り時刻")
      expect(weather_data).to have_key("日の出時刻")
      expect(weather_data).to have_key("日時") 
      expect(weather_data).to have_key("最低気温") 
      expect(weather_data).to have_key("最高気温") 
    end
  end
end

 

テストが通ったことを確認できたのでgemをパッケージ化する。

% rake build
weather_wizard 0.1.0 built to pkg/weather_wizard-0.1.0.gem.


続いて挙動の確認をする。インストールを試みるができない。

❯ gem install weather_wizard
ERROR: Could not find a valid gem 'weather_wizard' (>= 0) in any repository
ERROR: Possible alternatives: math_wizard, hash_wizard, step_wizard, weather-api, weather-report, weather-sage, weather_api_task, weather_data, weather_finder, weather_hash

 

理由を調べたところ、バージョンの指定がなかったことが原因だとわかった。以下のコマンドで成功。

% gem install pkg/weather_wizard-0.1.0.gem
Successfully installed weather_wizard-0.1.0
1 gem installed

gemのインストールはできたけれど、[gem名 + location]の入力で実行させることができない。

以下のコマンドでgemに含まれるファイルを確認。

 

% gem contents weather_wizard

 

実行ファイルが含まれていなかった。

実行権限つけて再ビルドするも変化なし。

 

% chmod +x bin/exec_weather_wizard.rb

 

% gem install pkg/weather_wizard-0.1.1.gem

 

Rubygemガイドには実行可能ファイルを含めるには、Gemspec に追加する必要という記載がある。

 

RubyGem Guide

 

gemspecの内容を見てみると以下の記述があった。

% spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }

 

これでは現状のファイルは実行対象に入っていない。

対象ファイルに修正。バージョンを変更し再ビルド等やってみるがうまくいかない。

結局、理由はシステムのコマンドパスにGem の実行ファイルを含めないと
「gem名+オプション」といったコマンドは有効にならないためだった。

実行ファイルはデフォルトのgemspecの記載に従い、
exeディレクトリ作成後、exec_weather_wizard.rbのファイルを作っておくことにした。

gemでは気象情報のハッシュが取得できる
→実行ファイルで取得するデータが確認できるようにした。

 

% ruby -Ilib ./exe/exec_weather_wizard.rb <location>

 

あわせて修正したい箇所が出てきたためリポジトリを再作成、gem名を変更。

意図した機能の実装完了。

-Productivity Tips, フィヨルドブートキャンプ