Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
外部APIが絡むテストをちょっといい感じに書く/a-little-nice-writing-e...
Search
Masatoshi Moritsuka
February 26, 2025
Programming
41
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
外部APIが絡むテストをちょっといい感じに書く/a-little-nice-writing-external-api-testing
Masatoshi Moritsuka
February 26, 2025
More Decks by Masatoshi Moritsuka
See All by Masatoshi Moritsuka
拙者、『型は欲しいが型は書きたくない』者たちとの和睦を結び、るびぃにおける型の領地安堵を実現せんと欲す者也 #sekigahara01/sekigahara01
sanfrecce_osaka
4
2.7k
Rails の CLI ツールの書き方/writing-rails-cli-tool
sanfrecce_osaka
0
87
Time.zone.parse('dark')/time-zone-parse-dark
sanfrecce_osaka
0
140
gem_rbs_collection へのコントリビュートから始める Ruby の型の世界/contributing-gem-rbs-collection
sanfrecce_osaka
0
680
Rails と人魚の話/rails-and-mermaid
sanfrecce_osaka
0
510
パターンマッチ使ってるかい?(kyobashi.rb)/use-ruby-s-pattern-matching-on-kyobashi-rb
sanfrecce_osaka
0
310
ApplicationController の継承を分割してエラーを減らした話/dividing-application-controller
sanfrecce_osaka
1
450
Input object ではじめる入力値検証/input-value-validation-using-input-object
sanfrecce_osaka
0
630
実例で学ぶRailsアプリケーションデバッグ入門 〜ログインできちゃってました編〜/rails-application-debug-introduction
sanfrecce_osaka
2
950
Other Decks in Programming
See All in Programming
AI-Powered Domain-Driven Design
ziobrando
0
190
AWSに止められる覚悟してますか?
morizo_1984
2
540
Rでドレミ/Do-Re-Mi_with_R
florets1
0
130
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
35
17k
FactoryBotをやめればRSpecは速くなるのか?
yuta_onishi_97
0
960
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
450
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
290
AI時代のコードレビューは人に向けるな、仕組みに向けろ
texmeijin
5
3.7k
sbt 2.0: takeoff
eed3si9n
0
170
Simple Storage Service(S3) is not simple
iwatsukayura
0
310
作って学ぶSpring BootのAuto Configuration / JJUG Night Seminar 2026-10-19
rshindo
0
200
No More NoSuchMethodError
exoego
1
110
Featured
See All Featured
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Git: the NoSQL Database
bkeepers
PRO
433
67k
Believing is Seeing
oripsolob
1
250
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
720
Heart Work Chapter 1 - Part 1
lfama
PRO
10
37k
We Are The Robots
honzajavorek
0
400
Site-Speed That Sticks
csswizardry
13
1.6k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
850
Reflections from 52 weeks, 52 projects
jeffersonlam
356
22k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
sira's awesome portfolio website redesign presentation
elsirapls
0
460
We Have a Design System, Now What?
morganepeng
55
8.4k
Transcript
外部API が絡む テストを ちょっといい感じに 書く 森塚 真年(@sanfrecce_osaka) 2025/02/26 Kyobashi.rb#5 #kyobashirb
自己紹介 森塚 真年 GitHub: @sanfrecce-osaka Twitter(X) ・Qiita: @sanfrecce_osaka 趣味: コミュニティ・勉強会
Machida.rb(next: 2/7( 金)) Hirakata.rb(next: 1/26( 日)) 株式会社GMO エンペイ Ruby3.3.6/Rails7.1( 次は3 月末までに 7.2 に上げ るぞ) Node.js v18/Vue.js 3.3/Vuetify 3.4
本編
外部API 絡むテスト
どのように 書いてますか?
stub_request(:post, 'https://example.com/') .to_return_json(body: { id: 1, foo: 'bar', baz: true
})
1 箇所だけなら まだいいけど…
stub_request(:post, 'https://example.com/') .to_return_json(body: { id: 1, foo: 'bar', baz: true
}) # ... stub_request(:post, 'https://example.com/') .to_return_json(body: { id: 1, foo: 'piyo', baz: true }) # ... stub_request(:post, 'https://example.com/') .to_return_json(body: { id: 1, foo: 'bar', baz: false }) .to_return_json(body: { error_code: 'EE01' })
つらい😇
中には
レスポンスが JSON じゃない API
stub_request(:post, 'https://example.com/') .to_return(body: URI.encode_www_form( ID: 1, Foo: 'bar', Baz #
... stub_request(:post, 'https://example.com/') .to_return(body: URI.encode_www_form( ID: 1, Foo: 'piyo', Ba # ... stub_request(:post, 'https://example.com/') .to_return(body: URI.encode_www_form( ID: 1, Foo: 'bar', Baz .to_return(body: URI.encode_www_form( ErrorCode: 'EE01' ))
つらい😇
そこで
FactoryBot
FactoryBot.define do factory :foo_bar_response, class: Hash do id { 1
} foo { 'bar' } bar { true } initialize_with { attributes.stringify_keys } end end
FactoryBot.define do factory :foo_bar_response, class: Hash, traits: %i[success] trait :success
do id { 1 } foo { 'bar' } bar { true } end trait :error do transient do id { nil } foo { nil } bar { nil } end
stub_request(:post, 'https://example.com/') .to_return_json(body: build(:foo_bar_response)) # ... stub_request(:post, 'https://example.com/') .to_return_json(body: build(:foo_bar_response,
foo: 'piyo' } # ... stub_request(:post, 'https://example.com/') .to_return_json(body: build(:foo_bar_response, baz: false }) .to_return_json(body: build(:foo_bar_response, :error, error
FactoryBot.define do factory :foo_bar_response, class: 'String', traits: %i[succe trait :success
do self.ID { 1 } self.Foo { 'bar' } self.Bar { true } end trait :error do transient do self.ID { nil } self.Foo { nil } self.Bar { nil } end
stub_request(:post, 'https://example.com/') .to_return(body: build(:foo_bar_response)) # ... stub_request(:post, 'https://example.com/') .to_return(body: build(:foo_bar_response,
Foo: 'piyo' }) # ... stub_request(:post, 'https://example.com/') .to_return(body: build(:foo_bar_response, Baz: false }) .to_return(body: build(:foo_bar_response, :error, ErrorCode:
文字列じゃなくて Hash が欲しい場合 attributes_for(:foo_bar_response) => { ID: id, Foo: foo,
Bar:
module ExternalApiStubHelper module Hoge module Fuga def mock_hoge_fuga(stub_hoge_fuga: nil, hoge_fuga_respon
hoge_fuga_request = stub_request(:post, 'https://examp stub_hoge_fuga ? stub_hoge_fuga.call(hoge_fuga_request end end end end
# frozen_string_literal: true module ExternalApiStubHelper include Hoge end
vcr
こんなことない? 外部に実際にリクエストしていて CI が遅い どこで呼ばれているかわからない外部 API のリク エスト 最近見つけたやつ: カード情報を消すための
trait
RSpec.describe 'Foo', type: :request do # ちょっとでも外部の API 叩くなら vcr
を true にする # WebMock で stub する前提でもとりあえず true にする describe 'GET xxx', vcr: true do # ... end end
VCR.configure do |c| # ... c.default_cassette_options = { # CI
では cassette を生成しないようにする record: ENV['CI'] ? :none : :new_episodes, # ... } end
ご清聴 ありがとうございました。
None