실용적인 테스트 중심 개발 방법
테스트 중심 개발이란 무엇인가요?
테스트 중심 개발(TDD)은 단순히 테스트를 먼저 작성하는 것을 의미합니다. 비즈니스 로직을 작성하기 전에 올바른 코드에 대한 기대치를 미리 설정할 수 있습니다. TDD는 코드가 올바른지 확인하는 데 도움이 될 뿐만 아니라 더 작은 함수를 작성하고 기능 중단 없이 코드를 리팩터링하며 문제를 더 잘 이해하는 데도 도움이 됩니다.
이 글에서는 작은 유틸리티 프로그램을 구축하여 TDD의 몇 가지 개념을 소개하겠습니다. 또한 TDD가 여러분의 삶을 더 쉽게 만들어 주는 몇 가지 실용적인 시나리오도 다룰 것입니다.
TDD를 사용하여 HTTP 클라이언트 구축
우리가 만들 것
우리는 다양한 HTTP 동사를 추상화하는 간단한 HTTP 클라이언트를 점차적으로 구축할 것입니다. 리팩토링을 원활하게 진행하기 위해 TDD 방식을 따를 것입니다. 테스트에는 Jasmine, Sinon 및 Karma를 사용하겠습니다. 먼저 샘플 프로젝트에서 package.json, karma.conf.js 및 webpack.test.js를 복사하거나 GitHub 리포지토리에서 직접 샘플 프로젝트를 복제합니다.
새로운 Fetch API의 작동 방식을 이해하면 도움이 되지만 이러한 예는 따라하기 쉽습니다. 초보자에게는 Fetch API가 XMLHttpRequest보다 더 나은 대안입니다. 네트워크 상호작용을 단순화하고 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
메소드를 호출합니다. 이 문제를 해결해 보겠습니다.
지금 테스트를 실행하면 预期 [object Response] 等于 Object({ })
。响应是一个 Stream 对象。顾名思义,流对象都是一个数据流。要从流中获取数据,您需要首先使用流的一些辅助方法来读取流。现在,我们可以假设流是 JSON 并通过调用 response.json()
deserializing이라는 실패한 응답이 표시됩니다.
이제 테스트 스위트는 녹색이 될 것입니다.
쿼리 매개변수 추가
지금까지 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과 매우 유사합니다.가 필요합니다. options
属性,您可以在其中定义标头、正文,以及最重要的 method
。该方法描述了 HTTP 动词,在本例中为 "post"
으아아아
이 시점에서 우리의방법은 매우 원시적입니다. JSON 요청 이외의 다른 것은 지원하지 않습니다. post
替代内容类型和 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Python 스크립트를 사용하여 Linux 환경에서 자동화된 테스트를 구현하는 방법 소프트웨어 개발의 급속한 발전과 함께 자동화된 테스트는 소프트웨어 품질을 보장하고 개발 효율성을 향상시키는 데 중요한 역할을 합니다. 간단하고 사용하기 쉬운 프로그래밍 언어인 Python은 강력한 이식성과 개발 효율성을 갖추고 있으며 자동화된 테스트에 널리 사용됩니다. 이 기사에서는 Python을 사용하여 Linux 환경에서 자동화된 테스트 스크립트를 작성하는 방법을 소개하고 특정 코드 예제를 제공합니다. Linux 환경의 자동화를 위한 환경 준비

현대 소프트웨어 개발에서 CI(지속적 통합)는 코드 품질과 개발 효율성을 향상시키는 중요한 방식이 되었습니다. 그중 Jenkins는 성숙하고 강력한 오픈 소스 CI 도구로, 특히 PHP 애플리케이션에 적합합니다. 다음 콘텐츠에서는 Jenkins를 사용하여 PHP 지속적 통합을 구현하는 방법을 살펴보고 특정 샘플 코드와 세부 단계를 제공합니다. Jenkins 설치 및 구성 먼저 Jenkins를 서버에 설치해야 합니다. 공식 웹사이트에서 최신 버전을 다운로드하여 설치하세요. 설치가 완료되면 관리자 계정 설정, 플러그인 설치, 작업 구성을 포함한 몇 가지 기본 구성이 필요합니다. 새 작업 만들기 Jenkins 대시보드에서 "새 작업" 버튼을 클릭하세요. "무료"를 선택하세요.

PHP 패키징 및 배포에 대한 모범 사례는 무엇입니까? 인터넷 기술의 급속한 발전으로 인해 PHP는 웹 사이트 개발에 널리 사용되는 오픈 소스 프로그래밍 언어로서 점점 더 많은 개발자가 프로젝트 배포의 효율성과 안정성을 향상시켜야 합니다. 이 문서에서는 PHP 패키징 및 배포에 대한 몇 가지 모범 사례를 소개하고 관련 코드 예제를 제공합니다. 버전 제어 도구 사용 Git, SVN 등과 같은 버전 제어 도구는 개발자가 코드 변경 사항을 효과적으로 관리하는 데 도움이 될 수 있습니다. 버전 제어 도구를 사용하여 코드를 쉽게 추적하고 롤백하여 모든 배포가

현재 소프트웨어 개발 프로세스에서 지속적인 통합(ContinuousIntegration)과 지속적인 전달(ContinuousDelivery)은 개발 팀이 제품 품질을 개선하고 전달 속도를 높이기 위한 핵심 관행이 되었습니다. 대규모 소프트웨어 기업이든 소규모 팀이든 상관없이 두 영역 모두에서 이점을 얻을 수 있습니다. 이 문서에서는 C# 개발자에게 지속적인 통합 및 지속적인 전달 방식에 대한 몇 가지 제안 사항을 제공합니다. 자동화된 빌드 및 테스트 자동화된 빌드 및 테스트는 지속적인 통합의 기초입니다. 만들다

Webman을 사용하여 웹 사이트의 지속적인 통합 및 배포를 달성합니다. 인터넷의 급속한 발전으로 인해 웹 사이트 개발 및 유지 관리 작업이 점점 더 복잡해졌습니다. 개발 효율성을 높이고 웹사이트 품질을 보장하기 위해서는 지속적인 통합과 배포가 중요한 선택이 되었습니다. 이 기사에서는 Webman 도구를 사용하여 웹 사이트의 지속적인 통합 및 배포를 구현하는 방법을 소개하고 몇 가지 코드 예제를 첨부합니다. 1. Webman이란 무엇입니까? Webman은 Java 기반 오픈 소스 지속적 통합 및 배포 도구입니다.

PHP 개발에서 코드 품질을 유지하는 것은 소프트웨어 안정성, 유지 관리 가능성 및 보안을 향상시키는 데 중요합니다. 코드 품질을 지속적으로 모니터링하면 문제를 사전에 식별하고 조기 수정을 촉진하며 문제가 프로덕션 단계에 이르지 못하도록 방지합니다. 이 기사에서는 Jenkins와 SonarQube를 사용하여 PHP 프로젝트에 대한 지속적인 모니터링 파이프라인을 설정하는 방법을 살펴보겠습니다. Jenkins: 지속적인 통합 서버 Jenkins는 빌드, 테스트 및 배포 프로세스를 자동화하는 오픈 소스 지속적인 통합 서버입니다. 이를 통해 개발자는 주기적으로 트리거되는 작업을 설정하고 일련의 작업을 수행할 수 있습니다. PHP 프로젝트의 경우 Jenkins 작업을 설정하여 다음 작업을 완료할 수 있습니다. 버전 제어 시스템에서 코드 확인

React 및 Jenkins를 사용하여 지속적인 통합 및 지속적인 배포를 통해 프런트 엔드 애플리케이션을 구축하는 방법 소개: 오늘날의 인터넷 개발에서 지속적인 통합 및 지속적인 배포는 개발 팀이 효율성을 향상하고 제품 품질을 보장하는 중요한 수단이 되었습니다. 널리 사용되는 프런트엔드 프레임워크인 React는 강력한 지속적 통합 도구인 Jenkins와 결합되어 지속적인 통합 및 지속적인 배포를 위한 프런트엔드 애플리케이션을 구축하기 위한 편리하고 효율적인 솔루션을 제공할 수 있습니다. 이 기사에서는 React와 Jenkins를 사용하여 지원하는 방법을 자세히 소개합니다.

제목: GitLab 지속적 통합의 코드 커버리지 분석 및 예제 소개: 소프트웨어 개발이 점점 더 복잡해짐에 따라 코드 커버리지 분석은 소프트웨어 테스트 품질을 평가하는 중요한 지표 중 하나가 되었습니다. 지속적인 통합을 사용하여 코드 적용 범위 분석을 수행하면 개발 팀이 코드 품질을 실시간으로 모니터링하고 소프트웨어 개발 효율성을 향상시키는 데 도움이 될 수 있습니다. 이 기사에서는 GitLab에서 지속적인 통합 코드 범위 분석을 수행하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. GitLab의 코드 커버리지 분석 1.1 코드 커버리지
