Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
TypeScript+ESLintで守る単体テストの品質
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
hiroto_0411
June 25, 2025
Programming
88
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
TypeScript+ESLintで守る単体テストの品質
Mita.ts #6での登壇資料です。
https://mitats.connpass.com/event/353424/
hiroto_0411
June 25, 2025
More Decks by hiroto_0411
See All by hiroto_0411
DynamoDBは怖くない!〜テーブル設計の勘所とテスト戦略〜
hyamazaki
1
300
大好きな「学び合い文化 」への貢献がしたい!
hyamazaki
0
16
Other Decks in Programming
See All in Programming
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
530
AIエージェントで 変わるAndroid開発環境
takahirom
2
710
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
390
【やさしく解説 設計編 #0】DDDのコード、読めるのに分からない人へ
panda728
PRO
2
280
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
110
Prismを使った型安全な暗号化_関数型まつり2026
_fhhmm
0
150
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
1k
吝嗇家のためのAI活用 / AI development for miser - ChatGPT + Issue Driven Development
tooppoo
0
190
Apache Hive: そしてCloud Native Lakehouseへ
okumin
1
170
改善しないと、タスクが回らない。 “てんこ盛りポジション” を引き継いだ情シスの、入社3ヶ月の業務改善録
krm963
0
120
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
360k
継続モナドとリアクティブプログラミング
yukikurage
3
630
Featured
See All Featured
Context Engineering - Making Every Token Count
addyosmani
9
1k
Between Models and Reality
mayunak
4
380
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
190
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.7k
Evolving SEO for Evolving Search Engines
ryanjones
0
240
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
470
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
The Cost Of JavaScript in 2023
addyosmani
55
10k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.3k
Transcript
TypeScript+ESLintで守る単体テストの品質 TypeScript+ESLintで守る単体テストの品質 ~型を明示しないテストが招いた落とし穴とその改善策~ 株式会社Schoo 山﨑 光都 Mita.ts #6 2025/06/25
自己紹介 株式会社Schoo 山﨑 光都 (やまざき ひろと) @hiroto_0411(X, Qiita) 業務内容 レガシーシステムのリプレイス
による「次世代プラットフォー ムの構築」 Golang/TypeScript TypeScript歴は1ヶ月ほど です 技術発信文化の醸成
今日話すこと TypeScript単体テストで型を明示的に指定する重要性 型を明示しないテストの問題点 ESLintによる解決策 ※単体テストはVitestを使っています
プロジェクト構成 BE (Go,モジュラーモノリス) ←→ BFF (TypeScript + Hono) ←→ FE(Nuxt)
BFFの役割 BEのAPIをFEが扱いやすい形に整形 gRPC通信処理 gRPC <-> TypeScriptのデータ変換 &整形 認証・認可ロジック BFFを採用した理由 BEはモジュラーモノリスアーキテク チャを採用ドメインごとのシンプル なAPIのみを提供しているためコン テキストがまたがる場合がある BFFがつなぎ役を担い、FEが扱いや すい形に整える
Domain/RepositoryとServiceの実装 // User型の定義 export type User = { userId: string;
name: string; }; // Domain/Repository層のインターフェース export interface IUserRepository { getUser( context: Context): Promise< User>; } // Service層の実装 export const userService = { async getUserByJWT( repository: IUserRepository, context: Context ): Promise< User> { // ユーザー情報をdomain/repositoryのinterfaceを呼び出して取得する const userResponse = await repository. getUser(context); return { ...userResponse, }; }, };
型を明示的に指定しない単体テストの問題点 // User型にorganizationが追加された export type User = { userId: string;
name: string; organization: { // ← 新しく追加 id: string; name: string; }; }; 仕様が変わりinterfaceの返り値が変わった
型を明示的に指定しない単体テストの問題点 // 問題のあるテストコード test( "repositoryからデータを取得できたとき、その値を返すこと", async () => { const
mockUser = { // 型指定なし userId: "user1", name: "テストユーザー" }; const mockRepository: IUserRepository = { getUser: vi. fn(). mockResolvedValue(mockUser), }; // テスト実行 const result: User = await userService. getUser(mockRepository, mockContext); // 検証 expect(result). toEqual({ ...mockUser, }); }); 問題点: interfaceが変更されてもテストでエラーにならない
型を明示的に指定しない単体テストの問題点 // vitestのmockResolvedValueの実装 mockResolvedValue( value: Awaited< ReturnType<T>>): this; interface MockInstance<T
extends Procedure = Procedure> 問題点: mockResolvedValue(value: Awaited<ReturnType<T>>): this で使う値 (mockUser)に明確な型定義がないため、戻り値型がanyとして解釈されてしま い、interfaceが変更された場合でも型エラーとなってくれない
解決策: テストコードの変数宣言時に型必須にするESLintを設定 // eslint.config.js { files: [ "**/*.test.ts", "**/*.spec.ts"], rules:
{ "@typescript-eslint/no-explicit-any": "error", //any型の使用を禁止 "@typescript-eslint/typedef": [ "error", { "variableDeclaration": true, } ], //変数宣言時に型注釈を必須 }, }
改善後のテストコード // 改善されたテストコード test( "repositoryからデータを取得できたとき、ユーザ情報と組織情報を返すこと", async () => { const
mockUser: User = { // 明示的な型指定 userId: "user1", name: "テストユーザー", organization: { id: 'org456', name: 'Schoo', }, }; const mockRepository: IUserRepository = { getUser: vi. fn(). mockResolvedValue(mockUser), }; // テスト実行 const result: User = await userService. getUser(mockRepository, mockContext); // 検証 expect(result). toEqual({ ...mockUser, }); }); interface変更時にテストでエラーが発生し、修正漏れを防げる
まとめ 単体テストでは変数宣言時に型を明示的に指定する → interface変更時の問題 (mockの問題など)を早期発見できる 型必須のルール設定 → チーム全体で一貫した、より安全で効果的な単体テストを 実装できる