実践的なテスト駆動開発手法
#テスト駆動開発とは何ですか?
テスト駆動開発 (TDD) とは、単に最初にテストを作成することを意味します。ビジネス ロジックのラインを作成する前に、正しいコードに対する期待値を事前に設定できます。 TDD は、コードが正しいことを確認するだけでなく、より小さな関数を作成し、機能を損なうことなくコードをリファクタリングし、問題をより深く理解するのにも役立ちます。
この記事では、小さなユーティリティ プログラムを構築することで、TDD の概念をいくつか紹介します。 TDD によって作業が楽になるいくつかの実践的なシナリオについても説明します。
TDD を使用した HTTP クライアントの構築
私たちが構築するもの
さまざまな HTTP 動詞を抽象化する単純な HTTP クライアントを徐々に構築していきます。リファクタリングをスムーズに進めるために、TDD プラクティスに従います。テストには Jasmine、Sinon、Karma を使用します。まず、サンプル プロジェクトから package.json、karma.conf.js、および webpack.test.js をコピーするか、GitHub リポジトリからサンプル プロジェクトを直接複製します。
新しい Fetch API がどのように機能するかを理解しておくと役立ちますが、これらの例は理解しやすいはずです。初心者にとっては、XMLHttpRequest の代わりに Fetch API の方が適しています。これにより、ネットワークのやり取りが簡素化され、Promise とうまく連携します。
パッケージを入手
まず、空のファイルを
src/http.js に作成し、付随するテスト ファイルを src/__tests__/http-test.js に作成します。
このサービスのテスト環境をセットアップしましょう。リーリー
ここでは Jasmine と Sinon を使用しています。Jasmine はテスト シナリオ、Sinon アサーション、および監視オブジェクトを定義します。 (Jasmine にはテストを監視しスタブ化する独自の方法がありますが、私は Sinon の API を好みます。)上記のコードは一目瞭然です。使用可能なサーバーがないため、各テストを実行する前に Fetch API の呼び出しをハイジャックし、モックの Promise オブジェクトを返します。ここでの目標は、Fetch API が正しいパラメーターで呼び出されるかどうかを単体テストし、ラッパーがネットワーク エラーを正しく処理するかどうかを確認することです。
失敗したテストケースから始めましょう:
リーリー
karma start を呼び出してテスト ランナーを開始します。
http には
get メソッドがないため、テストは明らかに失敗します。この問題を修正しましょう。
リーリー
Expected [object Response] が Object({ }) に等しいことが示されます。応答は Stream オブジェクトです。名前が示すように、ストリーム オブジェクトはデータ ストリームです。ストリームからデータを取得するには、まずいくつかのヘルパー メソッドを使用してストリームを読み取る必要があります。ここで、ストリームが JSON であると仮定し、
response.json() を呼び出して逆シリアル化することができます。
リーリー
クエリパラメータを追加する
これまでのところ、
get メソッドは、クエリ パラメーターを使用せずに単純な呼び出しを行うだけです。失敗するテストを作成して、クエリ パラメーターがどのように処理されるかを見てみましょう。
{ users: [1, 2], limit: 50, isDetailed: false } をクエリ パラメーターとして渡す場合、HTTP クライアントは
/api/v1/users/ ? へのネットワーク呼び出しを行う必要があります。 users=1&users=2&limit=50&isDetailed=false.
リーリー
get メソッドを拡張しましょう。
リーリー
ここでは、クエリ文字列ライブラリを使用しています。これは、さまざまなクエリ パラメーターのシナリオに役立つ小さなヘルパー ライブラリです。
突然変異の処理
GET は、おそらく最も簡単に実装できる HTTP メソッドです。 GET は冪等であるため、いかなる突然変異にも使用しないでください。 POST は通常、サーバー内のいくつかのレコードを更新することを意味します。これは、POST リクエストにはデフォルトで CSRF トークンなどの何らかの保護手段が必要であることを意味します。これについては次のセクションで詳しく説明します。
まず、基本的な POST リクエストのテストを構築しましょう:
リーリー
POST の署名は GET の署名と非常によく似ています。ヘッダー、本文、そして最も重要なmethod を定義する
options 属性が必要です。このメソッドは HTTP 動詞 (この場合は
"post") を記述します。
リーリー
現時点では、post メソッドは非常に原始的です。 JSON リクエスト以外はサポートされていません。
替代内容类型和 CSRF 令牌
让我们允许调用者决定内容类型,并将 CSRF 令牌投入战斗。根据您的要求,您可以将 CSRF 设为可选。在我们的用例中,我们将假设这是一个选择加入功能,并让调用者确定是否需要在标头中设置 CSRF 令牌。
为此,首先将选项对象作为第三个参数传递给我们的方法。
it("should send request with CSRF", done => { const postParams = { users: [1, 2 ] }; http.post(url, postParams, { contentType: http.HTTP_HEADER_TYPES.text, includeCsrf: true }).then(response => { const [uri, params] = [...stubedFetch.getCall(0).args]; expect(stubedFetch.calledWith(`${url}`)).toBeTruthy(); expect(params.body).toEqual(JSON.stringify(postParams)); expect(params.headers.get("Content-Type")).toEqual(http.HTTP_HEADER_TYPES.text); expect(params.headers.get("X-CSRF-Token")).toEqual(csrf); done(); }); });
当我们提供 options
和 {contentType: http.HTTP_HEADER_TYPES.text,includeCsrf: true
时,它应该相应地设置内容标头和 CSRF 标头。让我们更新 post
函数以支持这些新选项。
export const post = (url, params, options={}) => { const {contentType, includeCsrf} = options; const headers = new Headers(); headers.append("Content-Type", contentType || HTTP_HEADER_TYPES.json()); if (includeCsrf) { headers.append("X-CSRF-Token", getCSRFToken()); } return fetch(url, { headers, method: "post", body: JSON.stringify(params), }); }; const getCsrfToken = () => { //This depends on your implementation detail //Usually this is part of your session cookie return 'csrf' }
请注意,获取 CSRF 令牌是一个实现细节。通常,它是会话 cookie 的一部分,您可以从那里提取它。我不会在本文中进一步讨论它。
您的测试套件现在应该很满意。
编码形式
我们的 post
方法现在已经成型,但是在发送正文时仍然很简单。您必须针对每种内容类型以不同的方式处理数据。处理表单时,我们应该在通过网络发送数据之前将数据编码为字符串。
it("should send a form-encoded request", done => { const users = [1, 2]; const limit = 50; const isDetailed = false; const postParams = { users, limit, isDetailed }; http.post(url, postParams, { contentType: http.HTTP_HEADER_TYPES.form, includeCsrf: true }).then(response => { const [uri, params] = [...stubedFetch.getCall(0).args]; expect(stubedFetch.calledWith(`${url}`)).toBeTruthy(); expect(params.body).toEqual("isDetailed=false&limit=50&users=1&users=2"); expect(params.headers.get("Content-Type")).toEqual(http.HTTP_HEADER_TYPES.form); expect(params.headers.get("X-CSRF-Token")).toEqual(csrf); done(); }); });
让我们提取一个小辅助方法来完成这项繁重的工作。基于 contentType
,它对数据的处理方式有所不同。
const encodeRequests = (params, contentType) => { switch (contentType) { case HTTP_HEADER_TYPES.form: { return stringify(params); } default: return JSON.stringify(params); } } export const post = (url, params, options={}) => { const {includeCsrf, contentType} = options; const headers = new Headers(); headers.append("Content-Type", contentType || HTTP_HEADER_TYPES.json); if (includeCsrf) { headers.append("X-CSRF-Token", getCSRFToken()); } return fetch(url, { headers, method="post", body: encodeRequests(params, contentType || HTTP_HEADER_TYPES.json) }).then(deserializeResponse) .catch(error => Promise.reject(new Error(error))); };
看看那个!即使在重构核心组件之后,我们的测试仍然可以通过。
处理 PATCH 请求
另一个常用的 HTTP 动词是 PATCH。现在,PATCH 是一个变异调用,这意味着这两个操作的签名非常相似。唯一的区别在于 HTTP 动词。通过简单的调整,我们可以重用为 POST 编写的所有测试。
['post', 'patch'].map(verb => { describe(`Test ${verb} requests`, () => { let stubCSRF, csrf; beforeEach(() => { csrf = "CSRF"; stub(http, "getCSRFToken").returns(csrf); }); afterEach(() => { http.getCSRFToken.restore(); }); it("should send request with custom headers", done => { const postParams = { users: [1, 2] }; http[verb](url, postParams, { contentType: http.HTTP_HEADER_TYPES.text }) .then(response => { const [uri, params] = [...stubedFetch.getCall(0).args]; expect(stubedFetch.calledWith(`${url}`)).toBeTruthy(); expect(params.body).toEqual(JSON.stringify(postParams)); expect(params.headers.get("Content-Type")).toEqual(http.HTTP_HEADER_TYPES.text); done(); }); }); it("should send request with CSRF", done => { const postParams = { users: [1, 2 ] }; http[verb](url, postParams, { contentType: http.HTTP_HEADER_TYPES.text, includeCsrf: true }).then(response => { const [uri, params] = [...stubedFetch.getCall(0).args]; expect(stubedFetch.calledWith(`${url}`)).toBeTruthy(); expect(params.body).toEqual(JSON.stringify(postParams)); expect(params.headers.get("Content-Type")).toEqual(http.HTTP_HEADER_TYPES.text); expect(params.headers.get("X-CSRF-Token")).toEqual(csrf); done(); }); }); it("should send a form-encoded request", done => { const users = [1, 2]; const limit = 50; const isDetailed = false; const postParams = { users, limit, isDetailed }; http[verb](url, postParams, { contentType: http.HTTP_HEADER_TYPES.form, includeCsrf: true }).then(response => { const [uri, params] = [...stubedFetch.getCall(0).args]; expect(stubedFetch.calledWith(`${url}`)).toBeTruthy(); expect(params.body).toEqual("isDetailed=false&limit=50&users=1&users=2"); expect(params.headers.get("Content-Type")).toEqual(http.HTTP_HEADER_TYPES.form); expect(params.headers.get("X-CSRF-Token")).toEqual(csrf); done(); }); }); }); });
类似地,我们可以通过使动词可配置来重用当前的 post
方法,并重命名方法名称以反映通用的内容。
const request = (url, params, options={}, method="post") => { const {includeCsrf, contentType} = options; const headers = new Headers(); headers.append("Content-Type", contentType || HTTP_HEADER_TYPES.json); if (includeCsrf) { headers.append("X-CSRF-Token", getCSRFToken()); } return fetch(url, { headers, method, body: encodeRequests(params, contentType) }).then(deserializeResponse) .catch(error => Promise.reject(new Error(error))); }; export const post = (url, params, options = {}) => request(url, params, options, 'post');
现在我们所有的 POST 测试都已通过,剩下的就是为 patch
添加另一个方法。
export const patch = (url, params, options = {}) => request(url, params, options, 'patch');
很简单,对吧?作为练习,尝试自行添加 PUT 或 DELETE 请求。如果您遇到困难,请随时参考该存储库。
何时进行 TDD?
社区对此存在分歧。有些程序员一听到 TDD 这个词就逃跑并躲起来,而另一些程序员则靠它生存。只需拥有一个好的测试套件,您就可以实现 TDD 的一些有益效果。这里没有正确的答案。这完全取决于您和您的团队对您的方法是否满意。
根据经验,我使用 TDD 来解决需要更清晰的复杂、非结构化问题。在评估一种方法或比较多种方法时,我发现预先定义问题陈述和边界很有帮助。它有助于明确您的功能需要处理的需求和边缘情况。如果案例数量太多,则表明您的程序可能做了太多事情,也许是时候将其拆分为更小的单元了。如果需求很简单,我会跳过 TDD,稍后添加测试。
总结
关于这个话题有很多噪音,而且很容易迷失方向。如果我能给你一些临别建议的话:不要太担心 TDD 本身,而要关注基本原则。这一切都是为了编写干净、易于理解、可维护的代码。 TDD 是程序员工具带中的一项有用技能。随着时间的推移,您会对何时应用此方法产生直觉。
感谢您的阅读,请在评论部分告诉我们您的想法。
以上が実践的なテスト駆動開発手法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Python スクリプトを使用して Linux 環境で自動テストを実装する方法 ソフトウェア開発の急速な発展に伴い、自動テストはソフトウェアの品質を確保し、開発効率を向上させる上で重要な役割を果たしています。 Python はシンプルで使いやすいプログラミング言語として、移植性と開発効率が高く、自動テストで広く使用されています。この記事では、Python を使用して Linux 環境で自動テスト スクリプトを作成する方法を紹介し、具体的なコード例を示します。 Linux環境での自動化のための環境準備

最新のソフトウェア開発では、コードの品質と開発効率を向上させるために継続的インテグレーション (CI) が重要な実践となっています。その中でも、Jenkins は成熟した強力なオープンソース CI ツールであり、特に PHP アプリケーションに適しています。次のコンテンツでは、Jenkins を使用して PHP 継続的インテグレーションを実装する方法を詳しく説明し、具体的なサンプル コードと詳細な手順を示します。 Jenkins のインストールと構成 まず、Jenkins をサーバーにインストールする必要があります。公式 Web サイトから最新バージョンをダウンロードしてインストールするだけです。インストールが完了したら、管理者アカウントの設定、プラグインのインストール、ジョブの設定など、いくつかの基本的な設定が必要です。新しいジョブの作成 Jenkins ダッシュボードで、[新しいジョブ] ボタンをクリックします。 「フリーズ」を選択します

PHP のパッケージ化と展開のベスト プラクティスは何ですか? Web サイト開発で広く使用されているオープンソース プログラミング言語である PHP は、インターネット テクノロジの急速な発展に伴い、プロジェクト展開の効率と安定性を向上させる必要がある開発者がますます増えています。この記事では、PHP のパッケージ化と展開に関するいくつかのベスト プラクティスを紹介し、関連するコード例を示します。バージョン管理ツールを使用する Git、SVN などのバージョン管理ツールは、開発者がコードの変更を効果的に管理するのに役立ちます。バージョン管理ツールを使用してコードを簡単に追跡およびロールバックし、すべてのデプロイメントが確実に行われるようにします。

現在のソフトウェア開発プロセスでは、継続的インテグレーション (ContinuousIntegration) と継続的デリバリー (Continuousdelivery) が、開発チームが製品の品質を向上させ、デリバリーを迅速化するための重要な実践となっています。大規模なソフトウェア企業であっても、小規模なチームであっても、両方の分野からメリットを得ることができます。この記事では、C# 開発者に継続的インテグレーションと継続的デリバリーの実践に関するいくつかの提案を提供します。自動化されたビルドとテスト 自動化されたビルドとテストは、継続的インテグレーションの基礎です。作る

Webman を使用して Web サイトの継続的な統合と展開を実現する インターネットの急速な発展に伴い、Web サイトの開発と保守の作業はますます複雑になってきています。開発効率を向上させ、Web サイトの品質を確保するには、継続的な統合と展開が重要な選択肢となっています。この記事では、Webman ツールを使用して Web サイトの継続的統合と展開を実装する方法を紹介し、いくつかのコード例を添付します。 1. Webman とは何ですか? Webman は、Java ベースのオープンソースの継続的統合および展開ツールです。

タイトル: GitLab 継続的インテグレーションにおけるコード カバレッジ分析と例 はじめに: ソフトウェア開発がますます複雑になるにつれて、コード カバレッジ分析はソフトウェア テストの品質を評価する重要な指標の 1 つになりました。継続的インテグレーションを使用してコード カバレッジ分析を実施すると、開発チームがコードの品質をリアルタイムで監視し、ソフトウェア開発効率を向上させることができます。この記事では、GitLab で継続的統合コード カバレッジ分析を実行する方法を紹介し、具体的なコード例を示します。 1. GitLab でのコード カバレッジ分析 1.1 コード カバレッジ

PHP 開発では、ソフトウェアの信頼性、保守性、セキュリティを向上させるために、コードの品質を維持することが重要です。コードの品質を継続的に監視することで、問題を積極的に特定し、早期の修正を促進し、本番環境への影響を防ぎます。この記事では、Jenkins と SonarQube を使用して PHP プロジェクトの継続的な監視パイプラインをセットアップする方法を説明します。 Jenkins: 継続的統合サーバー Jenkins は、ビルド、テスト、展開プロセスを自動化するオープンソースの継続的統合サーバーです。これにより、開発者は定期的にトリガーされるジョブを設定し、一連のタスクを実行できます。 PHP プロジェクトの場合、次のタスクを完了するように Jenkins ジョブをセットアップできます: バージョン管理システムからコードをチェックアウトする

React と Jenkins を使用して、継続的インテグレーションと継続的デプロイメントを備えたフロントエンド アプリケーションを構築する方法 はじめに: 今日のインターネット開発において、継続的インテグレーションと継続的デプロイメントは、開発チームが効率を向上させ、製品の品質を確保するための重要な手段となっています。人気のフロントエンド フレームワークである React を、強力な継続的インテグレーション ツールである Jenkins と組み合わせることで、継続的インテグレーションと継続的デプロイのためのフロントエンド アプリケーションを構築するための便利で効率的なソリューションを提供できます。この記事ではReactとJenkinsを使ってサポートする方法を詳しく紹介します。
