テーマ、知りたいこと
テストや試験について最近学んでいます!
テストについては、単体, ユニット, シナリオなどあると思います。
試験は、負荷試験やペネトレーション、脆弱性などあります。
更には手動のデバッグテストもあります。(今のチームは基本ここです)
手動のデバッグテストだと、属人化したりテストに時間がかかるためコストがでかいと判断し、皆さんのプロジェクトでどうなのか知りたく質問しました。
一旦は、TDDで開発しようと考えているのですが、どういったテストを書いていくのか悩んでおります。
絶賛勉強中なので初歩的な質問で恐縮ですが、意見いただけると嬉しいです。
入れてよかったテスト、実施して良かった試験、次は入れるテスト、試験をしなくて苦労した話などなど
教えてほしいです!
(回答に対して追加で質問するかもでしつこく聞いちゃったらすみません...)
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
回答11件
#2
総合スコア21596
投稿2026/03/05 01:59
テスト駆動開発 単行本(ソフトカバー) – 2017/10/14
Kent Beck (著), 和田 卓人 (翻訳)
まずはこれを買って読んでください。
そうでなければお話になりません。
自動テストやテスト駆動開発というと何か難しい響きがあるので及び腰になる気持ちはわかります。
しかし、その実態は実にシンプル。
srcディレクトリ配下に関数やクラス・メソッドなんかを含めたコードを配置していきますが、
本筋のコード達が使うのとは別にsrcと同列のディレクトリに存在するtestディレクトリ配下から、
src配下のソースコードを別ルートでロードしちゃって、色んな値を関数やクラス・メソッドに渡して実行させるというだけの代物です。
実行するだけならばとても単純です。
しかし、それを開発の現場で役立てようとすると牙を剥いてきます。
実際の開発では大量のモジュールやライブラリを組み合わせ、最終的に結合することになりますからね。
処理郡を適切に管理して純粋関数や副作用のないメソッドを切り出して適切な名前をつけて管理する能力がダイレクトに問われます。
この辺はリーダブルコードや関数型言語の理解が進んでいくに従って解消されるかと思いますが、チームメンバー全員の能力と理解が求められます。
特にリーダーやCTO辺りが「動けばよくね?」とか言ってる職場はもう無理なので逃げた方が良いです。
#3
まずはこれを買って読んでください。
そうでなければお話になりません。
注文しました!!
処理郡を適切に管理して純粋関数や副作用のないメソッドを切り出して適切な名前をつけて管理する能力がダイレクトに問われます。
この辺はリーダブルコードや関数型言語の理解が進んでいくに従って解消されるかと思いますが、チームメンバー全員の能力と理解が求められます。
リーダブルコードは読みました!
上長から関数型もキャッチアップを推奨されていたので、そちらもなにか本を読みたいと思います!
(おすすめあれば教えて下さい...🙇♂️)
#4
総合スコア4164
投稿2026/03/05 08:43
から関数型もキャッチアップを推奨されていたので
わたしは Scala https://docs.scala-lang.org/ja/ を勉強したときに、関数型プログラミングの一端を掴んだ気がします。実務でのScala出番はいまのところほぼありませんが、掴んだものは活かせているとおもってます。
#5
総合スコア133
投稿2026/03/05 08:44
編集2026/03/05 08:44テストは書いてます!特にユニットテストです。ほんとはインテグレーションテストもE2Eテストも書きたいです!
TDDとは別の理由ですが、うちは事業会社でWebサービスを提供しています。最近は市場投入のスピードが事業部として求められているのですが、市場投入のスピードを速くするという観点でもテストが大事で、テスト容易性の高いアーキテクチャをつくるようにもしています。テスト容易性が高いほど「変更→確認→リリース」のサイクルが速く回るからです。
#6
総合スコア285
投稿2026/03/05 08:59
関数型
まぁ、関数型と言うよりも参照透過性だな。
要は、参照透過性に違反してるようなコード(これは現実には結構あるだろう)に対してTDDはどう対応してんだろ?
#2 氏が挙げてる本にそれに対して記述があればいいんだけど。僕は読んだ事がないんで、この辺はなんとも言えない。
単純には次のような事だ。
例えば乱数を利用したプログラムをどうTDDすんのか?シードを固定する、ってのも一つの手なんだけど、テスト通ったら戻すのかな(笑)?理屈ではその通り、なんだろうけど個人的にはピンとこない。
あと、例えば「状態を持つ関数」とかはどうなんだろ。いわゆるジェネレータとかコルーチンの類だ。
例えばこんなカンジだよな。
Python
1#!/usr/bin/env python3 2 3from itertools import count 4 5def primes(): 6 yield 2 7 for n in count(3): 8 if isPrime(n): 9 yield n 10 11def isPrime(n): 12 for ps in primes(): 13 if ps ** 2 > n: 14 return True 15 elif n % ps == 0: 16 return False 17 else: 18 continue
例えばこれはこういう風に使う。
Python
1>>> prime = primes() 2>>> next(prime) 32 4>>> next(prime) 53 6>>> next(prime) 75 8>>> next(prime) 97 10>>> next(prime) 1111
つまり、ジェネレータprimesを代入したprimeは事実上、「状態を持った」関数になっている。従って、実行する度に「違う結果」を返してきて、要は参照透過性に違反している。
そして返り値には「規則性がない」。
こんなのにTDDはどう対応するのか。僕の知識不足もあり得るんだけど、ちょっと疑念なんだよね(笑)。
あるいは副作用目的の関数だとどうするのか、とか、ツッコミどころがある。副作用目的の関数、いわゆるvoid型が返る関数とかどうすんの?とかね。いや、僕は関数型言語信者(笑)だからいいんだけど、一般的にはみんな、副作用目的の関数大好きじゃない(笑)。
必ず返り値を返すようにしましょう、ってのもアリなんだけど、
Python
1def Hello(): 2 print("Hello, Wolrd!") 3 return True
とかあんま意味ねぇよなぁ(笑)。
あるいは
Python
1def Hello(): 2 return print("Hello, Wolrd!")
とかする?やってもいいけど、あんま意味ないよね。
はてさて。
関数型もキャッチアップを推奨されていたので、そちらもなにか本を読みたい
JavaScriptなんかにも関数型プログラミングへの誘い、的な本があるにはあるんだけど、ECMAScriptの仕様の更新が1年毎で早いんで、書籍系はあっという間に古くなって、すぐ適さなくなっちゃう。
一番いいのは、純粋関数型言語であるHaskell対象のこの本かな?多分。
#7
総合スコア133
投稿2026/03/05 14:07
つまり、ジェネレータprimesを代入したprimeは事実上、「状態を持った」関数になっている。従って、実行する度に「違う結果」を返してきて、要は参照透過性に違反している。
そして返り値には「規則性がない」。
こんなのにTDDはどう対応するのか。
参照透過ではないですが、生成される数列自体は決定論的なので、以下のようにテストを作成することはできました。
この辺りはTDDを適用することはできそうですね
python
1from itertools import islice 2 3from src.app.main import primes 4 5def test_primes(): 6 result = list(islice(primes(), 10)) 7 assert result == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
副作用目的の関数についてはテストしないんじゃないですかね
うちだとオニオンアーキテクチャでやっていますが、Repositoryはinterfaceで抽象化し、UseCaseのテストを実装する際は本番用のRepositoryの実装クラスとテスト用のRepositoryの実装クラス(中身はモック)としてテストしていますね。副作用のあるクラスをinterfaceにすることでテストできるようにする、みたいなテクニックは使っています。副作用は外に追い出して隠蔽し、テスト可能な状態にすることでTDDできるようにしています。
#8
総合スコア285
投稿2026/03/05 17:16
参照透過ではないですが、生成される数列自体は決定論的なので、以下のようにテストを作成することはできました。
この辺りはTDDを適用することはできそうですね
「現実的な」話をすればそうなるでしょうね。
ただ、概念的な話をすれば、どうしても、TDDには「それでいいの?」って話が出てくると思う。この辺、アイディアルな立場なのか、あるいはプラグマティックな立場なのか、で様相が変わるかな。
例えばジェネレータprimesは「終わりの無い」整数列を延々と返す。そしてその「計算が本当に全て正しいのか」全く確かめる術がない。
つまり、「テストファースト」って前提がそもそも理論的には崩れる、って事になる。
もう一つの問題は、・・・いや、現実的な話をすると、例えば参照透過性が無い関数で、[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]を順次返す関数を書け、と。要は最小値と最大値が決まってるパターンだね。
その場合、僕は極めて不マジメな人間なんで、恐らく#6 で書いたようなジェネレータじゃなくって次のようなジェネレータを書いて終わるかも。
Python
1#!/usr/bin/env python3 2 3def primes(): 4 stack = [29, 23, 19, 17, 13, 11, 7, 5, 3, 2] 5 while True: 6 yield stack.pop()
こっちの方がでっち上げるのはラクだし(笑)。29以上に関しては「テストにないから知らんがな」と(笑)。
つまりね。全計算に於いて「バグがない関数をTDDで書こう」なんじゃなくって「テストを通す為に問題そのものがすり替わってしまう」可能性があるんだよ。結果、目的と手段が入れ替わっちまう。
まぁ、みんなもっとマジメだからマジメなコーディングするだろ、とは思うんだけど、僕みたいな不埒モノがいると(笑)、基本的に崩壊しちまう手段なんだよね。全然自分を信用してないんで(笑)、そっちの危機感の方が大きいです。
いずれにせよ、その辺の、TDDが保証する「正しさ」とは何なのか、と。あるいは、「正しさ」より「割り切り」っつーかな。上の「リスト(スタック)からpopするのもアリだよ。納期もあるし。」って捉えるのとでは、立場が変わるのでは、ってのが僕が考えてる事です。
副作用目的の関数についてはテストしないんじゃないですかね
それも「現実的」かな。
余談だけど、「プログラミングをやってみたい」って人で、ちょっとマジメそうな人にはいっつも勧めているプログラミングの基礎って本があるんだけど。この本って一貫して「テストファースト」でやっている。面白い。
ただ、実は#6 で書いたような「弱点」を著者が把握してるのか、実はこの本、乱数を使ったプログラムが一切無いんだ(笑)。それどころか、一切「入力」や「出力」に付いても書かれていない(笑)。もうホント、ちょっとだけ代入に付いて書かれてるけど、基本的には参照透過性を重視して副作用を使わないようにしている。
僕は大好きなスタイルなんだけど、果たして一般には、そこまでストイックなスタイルで突き詰められるのか、あるいは突き詰める必要があるのか、は分からないですね。
なかなか、TDDは僕にとっては悩ましい存在です。
#9
総合スコア220
投稿2026/03/05 22:46
編集2026/03/05 22:52「テストコード」は書いてないです。
色々話されてますが、そもそもテストコードを書くためにコードを整理しないといけなくて、それが本質的に無駄だと気づいたからです。
大規模なプロジェクトでテスト工数に数ヶ月かかるとかなら、テストコードを書いた方が時間効率がいいと思いますが。
うちだとオニオンアーキテクチャでやっていますが、Repositoryはinterfaceで抽象化し、UseCaseのテストを実装する際は本番用のRepositoryの実装クラスとテスト用のRepositoryの実装クラス(中身はモック)としてテストしていますね。副作用のあるクラスをinterfaceにすることでテストできるようにする、みたいなテクニックは使っています。副作用は外に追い出して隠蔽し、テスト可能な状態にすることでTDDできるようにしています。
これ、DIって言うんだよね。
あとは依存性逆転とかは、テストしないとしても、副作用がどこにあるのかわかるから便利なんだよね
ステートフルな関数を探すっていう苦行から開放されるんだよね
#10
総合スコア5863
投稿2026/03/07 13:09
私はプロのプログラマではないので業務上の感覚はわからないと先にお断りしておきます。
私は過去に機械製品の品質管理をやっていた経験から問題は想定外のところから出てくるという感覚を強く持っており、事前にテストをしっかりと作りこむのは無理だという意識があります。 ただ、それはテストに意味がないというわけではなく、テストに意味はあるけど適切なテストを事前には作れないという意味です。
事前には作れないので事後に作るしか仕方ないです。 (もちろん想定できる部分については事前になるべく作れば良いですが。) 起きた問題をテストに追加していって少なくとも後戻りはしないことによって品質を高めるのです。 あるプロジェクトで作ったテストは次のプロジェクトにも活用できることは多いでしょうし、そういった知見を蓄積していけば大事な資産になっていくでしょう。
テストドリブンで運用していきなり最初から問題をどんどん検出できるなんてことは期待できないと思います。
テストの具体的な内容とは別に、テストしやすい構成にすることはプロジェクトの最初からやったほうが良いでしょう。
#11
総合スコア21853
投稿2026/03/07 14:46
参照透過性がないとテストできるのか心配している方もいるようですが、副作用があっても普通にやってます。副作用を伴う処理(副作用がある関数呼び出しなど)で状態が変化するのにどうやって?と思うかもしれませんが、基本、一つ一つのテストは次のような流れです。
- 状態を初期化する。
- テストしたい処理をする。
- 処理の戻り値を検証する。
- 処理によって変化した内容を検証する。
- 変化した状態を破棄する(元に戻す)。
参照透過な処理であれば2.と3.だけで終わるのですが、副作用があってもこのようにすることで、副作用自体をテストし、かつ、次のテストでその副作用の影響を受けないようにします。並列でテストを実行するという場合は、この状態自体をそれぞれスレッドで独立するようにして、他の副作用の影響を受けないようにします。
面倒だなって思いませんでしたか?まぁ、大抵の場合、TDDに対応したフレームワークを使っていれば、上記のことを自動的、または、簡単に行うための仕組みが備わっています。
Ruby on Railsの例を取りましょう。MVCのコントローラーの部分のテストで、POSTによりモデルが作成されることをテストしたいとします。
ruby
1test "should_create_book" do 2 assert_difference("Book.count") do 3 post books_url, params: {book: {title: "走れメロス", author: "太宰治"}} 4 end 5 assert_redirected_to book_path(Book.last) 6 assert_equal "走れメロス", Book.last.title 7end
上記で、BookモデルをPOSTで作成をしようとしたとき、DB上でBookのレコードが一つ増える(Book.countが一つ増える)こと、POSTの返答が新しいBook(DB上に最後に作成されたBook)へのリダイレクトであること、その新しいBookのtitleが「走れメロス」であることを検証しています。
もし、このテストを何度も実行したら、DBが走れメロスでいっぱいになるのではと思ってしまうかもしれませんが、そうはなりません。なぜなら、Railsのテストでは、(必要な場合は)一つのテストが終わるたびにDBをロールバックして、レコードが追加される前の状態に戻すからです。さらに、テストを並列で行う場合は、テスト用のDBを並列するスレッド数分クローンして、それぞれ、独立したDBを使うようにします。DBへの書き込みという特大の副作用があるテストでも、このようにすることで、副作用の影響を与えずにテストすることが可能となっています。
DBを毎回ロールバックするなんて重すぎ・・・と思ったかと思います。ということで、そういう最終的には副作用がある処理であっても、副作用がない処理としてテストするという方法をとるフレームワークもあります。
下記はHanamiでのコントローラーのテストです。同じようにPOSTでBookを作る場合です。(Railsの動作とは異なり、リダイレクトではなく、jsonを返すという動作です)
ruby
1RSpec.describe Bookshelf::Actions::Books::Create do 2 subject(:action) do 3 Bookshelf::Actions::Books::Create.new(book_repo: book_repo) 4 end 5 6 let(:book_repo) do 7 instance_double(Bookshelf::BookRepo) 8 end 9 10 let(:book_params) do 11 {title: "走れメロス", author: "太宰治"} 12 end 13 14 it "returns a successful response when valid book params are provided" do 15 expect(book_repo).to receive(:create).with(book_params).and_return(book_params) 16 17 response = action.call(book: book_params) 18 19 expect(response).to be_successful 20 expect(response.body[0]).to eq(book_params.to_json) 21 end 22end
Hanamiは依存度注入を多用するフレームワークです。基本的には、コントローラー(action)はDBへの操作の公開インターフェースになっているレポジトリ(repo)に依存するように作成します。コントローラーとしては、レポジトリにcerateを投げられればよく、createが返してきた値に対して適切に処理できれば問題ありません。そこで、レポジトリをモックにすれば、DBへの処理はせずに、依存しているレポジトリのメソッドを実行するのか、その戻り値を適切に処理できたのかのテストができることになります。つまり、依存するなにかに副作用があったから副作用が発生するので、その依存するなにかを副作用がないものにすれば、副作用がなくなる、いわば、参照透過な処理としてテストできるということです。
上記とは別に、Hanamiでも、DBに対して処理が行われたかの検証をして、その後ロールバックするというテストも可能です。レポジトリはDBに依存してるので、さすがにそこを副作用を前提にテストを書くしか無いと思います。
多くのフレームワークでは、テストのための環境も最初から用意されているため、なにもないところから作成する場合よりも、TDDは始めやすいのかもしれません。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。