테스트 및 유지 관리가 가능한 PHP 코드 생성
Frameworks는 신속한 애플리케이션 개발을 위한 도구를 제공하지만 기능을 더 빠르게 생성할수록 기술적 부채가 늘어나는 경우가 많습니다. 기술 부채는 유지 관리 가능성이 개발자의 의도적인 초점이 아닐 때 발생합니다. 단위 테스트 및 구조가 부족하여 향후 변경 및 디버깅에 비용이 많이 듭니다.
테스트 용이성과 유지 관리 용이성을 위해 코드 구조화를 시작하고 시간을 절약하는 방법은 다음과 같습니다.
(느슨하게) 커버하겠습니다
- 건조
- 의존성 주입
- 인터페이스
- 컨테이너
- 단위 테스트에 PHPUnit을 사용하세요
인위적이지만 일반적인 코드부터 시작해 보겠습니다. 이는 특정 프레임워크의 모델 클래스일 수 있습니다.
으아악이 코드는 작동하지만 개선이 필요합니다.
- 테스트할 수 없습니다.
- 우리는
$_SESSION
全局变量。单元测试框架(例如 PHPUnit)依赖于命令行,其中$_SESSION
및 사용할 수 없는 다른 많은 전역 변수에 의존합니다. - 우리는 데이터베이스 연결에 의존합니다. 이상적으로는 단위 테스트에서 실제 데이터베이스 연결을 피해야 합니다. 테스트는 데이터가 아니라 코드에 관한 것입니다.
- 우리는
- 이 코드의 유지 관리 가능성은 이상적이지 않습니다. 예를 들어, 데이터 소스를 변경하면 애플리케이션에 사용되는 모든
App::db
인스턴스에서 데이터베이스 코드를 변경해야 합니다. 또한, 현재 사용자의 정보를 원하지 않으면 어떻게 되나요?
시도해볼 단위 테스트
위 기능에 대한 단위 테스트를 생성하려는 시도는 다음과 같습니다.
으아악 확인해 보겠습니다. 첫째, 테스트가 실패합니다. User
对象中使用的 $_SESSION
명령줄에서 PHP를 실행하기 때문에 단위 테스트에는 변수가 존재하지 않습니다.
둘째, 데이터베이스 연결 설정이 없습니다. 즉, 이 작업을 수행하려면 애플리케이션을 부트스트랩하여 App
对象及其 db
개체를 가져와야 합니다. 또한 테스트하려면 작동 중인 데이터베이스 연결이 필요합니다.
이 단위 테스트를 작동하려면 다음이 필요합니다.
- 애플리케이션에서 실행되는 CLI(PHPUnit)에 대한 구성 설정
- 데이터베이스 연결에 따라 다릅니다. 이렇게 한다는 것은 단위 테스트와 별개의 데이터 소스에 의존한다는 것을 의미합니다. 테스트 데이터베이스에 예상한 데이터가 없으면 어떻게 되나요? 데이터베이스 연결이 느리면 어떻게 되나요?
- 부트스트래핑에 의존하는 애플리케이션은 테스트에 오버헤드를 추가하여 단위 테스트 속도를 크게 늦출 수 있습니다. 이상적으로는 대부분의 코드를 사용된 프레임워크와 독립적으로 테스트할 수 있습니다.
이제 개선 방법에 대해 논의해 보겠습니다.
코드를 건조하게 유지하세요
이 간단한 맥락에서 현재 사용자를 검색하는 기능은 불필요합니다. 이것은 인위적인 예이지만 DRY 원칙의 정신에 따라 제가 선택한 첫 번째 최적화는 이 접근 방식을 일반화하는 것이었습니다.
으아악이는 애플리케이션 전체에서 사용할 수 있는 방법을 제공합니다. 모델에 함수를 전달하는 대신 호출 시 현재 사용자를 전달할 수 있습니다. 코드는 세션 전역 변수와 같은 다른 기능에 의존하지 않을 때 더 모듈화되고 유지 관리하기 쉽습니다.
그러나 이는 여전히 예상대로 테스트 및 유지 관리될 수 없습니다. 우리는 여전히 데이터베이스 연결에 의존하고 있습니다.
의존성 주입
일부 종속성 주입을 추가하여 이 상황을 개선하도록 돕겠습니다. 데이터베이스 연결을 클래스에 전달할 때 모델은 다음과 같습니다.
으아악이제 User
모델의 종속성이 제공됩니다. 우리 클래스는 더 이상 특정 데이터베이스 연결을 가정하지 않으며 전역 개체에 의존하지도 않습니다.
이 시점에서 우리 수업은 기본적으로 테스트 준비가 되었습니다. 선택한(주로) 데이터 소스와 사용자 ID를 전달하고 해당 호출의 결과를 테스트할 수 있습니다. 또한 별도의 데이터베이스 연결을 전환할 수도 있습니다(둘 다 동일한 데이터 검색 방법을 구현한다고 가정). 시원한.
단위 테스트가 어떤 모습인지 살펴보겠습니다.
으아악이 단위 테스트에 새로운 것을 추가했습니다: 조롱. Mockery를 사용하면 PHP 개체를 "모의"(가짜)할 수 있습니다. 이 예에서는 데이터베이스 연결을 시뮬레이션합니다. 모의를 사용하면 데이터베이스 연결 테스트를 건너뛰고 모델을 간단히 테스트할 수 있습니다.
조롱에 대해 더 알고 싶으세요?
이 예에서는 SQL 연결을 시뮬레이션하고 있습니다. 우리는 모의 객체에게 select
、where
、limit
和 get
方法。我返回 Mock 本身,以反映 SQL 连接对象如何返回自身 ($this
),从而使其方法调用“可链接”。请注意,对于 get
方法,我返回数据库调用结果 - 填充了用户数据的 stdClass
객체에 대한 호출을 예상하도록 지시합니다.
이렇게 하면 몇 가지 문제가 해결됩니다.
- 我们仅测试我们的模型类。我们还没有测试数据库连接。
- 我们能够控制模拟数据库连接的输入和输出,因此可以可靠地测试数据库调用的结果。我知道由于模拟数据库调用,我将获得用户 ID“1”。
- 我们不需要引导我们的应用程序,也不需要提供任何配置或数据库来进行测试。
我们还可以做得更好。这就是它变得有趣的地方。
接口
为了进一步改进这一点,我们可以定义并实现一个接口。考虑以下代码。
interface UserRepositoryInterface { public function getUser($user_id); } class MysqlUserRepository implements UserRepositoryInterface { protected $_db; public function __construct($db_conn) { $this->_db = $db_conn; } public function getUser($user_id) { $user = $this->_db->select('user') ->where('id', $user_id) ->limit(1) ->get(); if ( $user->num_results() > 0 ) { return $user->row(); } return false; } } class User { protected $userStore; public function __construct(UserRepositoryInterface $user) { $this->userStore = $user; } public function getUser($user_id) { return $this->userStore->getUser($user_id); } }
这里发生了一些事情。
- 首先,我们为用户数据源定义一个接口。这定义了
addUser()
方法。 - 接下来,我们实现该接口。在本例中,我们创建一个 MySQL 实现。我们接受一个数据库连接对象,并使用它从数据库中获取用户。
- 最后,我们在
User
模型中强制使用实现UserInterface
的类。这样可以保证数据源始终有一个可用的getUser()
方法,无论使用哪个数据源来实现UserInterface
。
请注意,我们的
User
对象类型提示UserInterface
在其构造函数中。这意味着实现UserInterface
的类必须传递到User
对象中。这是我们所依赖的保证 - 我们需要getUser
方法始终可用。
这样做的结果是什么?
- 我们的代码现在完全可测试。对于
User
类,我们可以轻松地模拟数据源。 (测试数据源的实现将是单独的单元测试的工作)。 - 我们的代码更加易于维护。我们可以切换不同的数据源,而无需更改整个应用程序的代码。
- 我们可以创建任何数据源。 ArrayUser、MongoDbUser、CouchDbUser、MemoryUser 等
- 如果需要,我们可以轻松地将任何数据源传递到我们的
User
对象。如果您决定放弃 SQL,则只需创建一个不同的实现(例如MongoDbUser
)并将其传递到您的User
模型中。
我们还简化了单元测试!
<?php use Mockery as m; use Fideloper\User; class ThirdUserTest extends PHPUnit_Framework_TestCase { public function testGetCurrentUserMock() { $userRepo = $this->_mockUserRepo(); $user = new User( $userRepo ); $result = $user->getUser( 1 ); $expected = new StdClass(); $expected->id = 1; $expected->username = 'fideloper'; $this->assertEquals( $result->id, $expected->id, 'User ID set correctly' ); $this->assertEquals( $result->username, $expected->username, 'Username set correctly' ); } protected function _mockUserRepo() { // Mock expected result $result = new StdClass(); $result->id = 1; $result->username = 'fideloper'; // Mock any user repository $userRepo = m::mock('Fideloper\Third\Repository\UserRepositoryInterface'); $userRepo->shouldReceive('getUser')->once()->andReturn( $result ); return $userRepo; } }
我们已经完全取消了模拟数据库连接的工作。相反,我们只是模拟数据源,并告诉它当调用 getUser
时要做什么。
但是,我们仍然可以做得更好!
容器
考虑我们当前代码的用法:
// In some controller $user = new User( new MysqlUser( App:db->getConnection("mysql") ) ); $user->id = App::session("user->id"); $currentUser = $user->getUser($user_id);
我们的最后一步是引入容器。容器。在上面的代码中,我们需要创建并使用一堆对象来获取当前用户。此代码可能散布在您的应用程序中。如果您需要从 MySQL 切换到 MongoDB,您仍然需要编辑上述代码出现的每个位置。那几乎不是干的。容器可以解决这个问题。
容器只是“包含”一个对象或功能。它类似于应用程序中的注册表。我们可以使用容器自动实例化一个新的 User
对象以及所有需要的依赖项。下面,我使用 Pimple,一个流行的容器类。
// Somewhere in a configuration file $container = new Pimple(); $container["user"] = function() { return new User( new MysqlUser( App:db->getConnection('mysql') ) ); } // Now, in all of our controllers, we can simply write: $currentUser = $container['user']->getUser( App::session('user_id') );
我已将 User
模型的创建移至应用程序配置中的一个位置。结果是:
- 我们的代码保持干燥。
User
对象和选择的数据存储在我们应用程序的一个位置定义。 - 我们可以将
User
模型从使用 MySQL 切换到 ONE 位置中的任何其他数据源。这更易于维护。
最终想法
在本教程的过程中,我们完成了以下任务:
- 保持我们的代码干燥且可重用
- 创建了可维护的代码 - 如果需要,我们可以在整个应用程序的一个位置切换对象的数据源
- 使我们的代码可测试 - 我们可以轻松模拟对象,而无需依赖引导我们的应用程序或创建测试数据库
- 了解如何使用依赖注入和接口来创建可测试和可维护的代码
- 了解容器如何帮助我们的应用程序更易于维护
我相信您已经注意到,我们以可维护性和可测试性的名义添加了更多代码。可以对这种实现提出强有力的论据:我们正在增加复杂性。事实上,这需要项目的主要作者和合作者对代码有更深入的了解。
但是,技术债务总体减少远远超过了解释和理解的成本。
- 代码的可维护性大大提高,可以在一个位置而不是多个位置进行更改。
- 能够(快速)进行单元测试将大幅减少代码中的错误 - 特别是在长期或社区驱动的(开源)项目中。
- 提前做额外的工作将节省时间并减少以后的麻烦。
资源
您可以使用 Composer 轻松地将 Mockery 和 PHPUnit 包含到您的应用程序中。将这些添加到 composer.json
文件中的“require-dev”部分:
"require-dev": { "mockery/mockery": "0.8.*", "phpunit/phpunit": "3.7.*" }
然后,您可以按照“dev”要求安装基于 Composer 的依赖项:
$ php composer.phar install --dev
在 Nettuts+ 上了解有关 Mockery、Composer 和 PHPUnit 的更多信息。
- 嘲笑:更好的方法
- 使用 Composer 轻松进行包管理
- 测试驱动的 PHP
对于 PHP,请考虑使用 Laravel 4,因为它特别利用了容器和此处介绍的其他概念。
感谢您的阅读!
위 내용은 테스트 및 유지 관리가 가능한 PHP 코드 생성의 상세 내용입니다. 자세한 내용은 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)

뜨거운 주제











Laravel은 직관적 인 플래시 방법을 사용하여 임시 세션 데이터 처리를 단순화합니다. 응용 프로그램에 간단한 메시지, 경고 또는 알림을 표시하는 데 적합합니다. 데이터는 기본적으로 후속 요청에만 지속됩니다. $ 요청-

PHP 클라이언트 URL (CURL) 확장자는 개발자를위한 강력한 도구이며 원격 서버 및 REST API와의 원활한 상호 작용을 가능하게합니다. PHP CURL은 존경받는 다중 프로모토콜 파일 전송 라이브러리 인 Libcurl을 활용하여 효율적인 execu를 용이하게합니다.

Laravel은 간결한 HTTP 응답 시뮬레이션 구문을 제공하여 HTTP 상호 작용 테스트를 단순화합니다. 이 접근법은 테스트 시뮬레이션을보다 직관적으로 만들면서 코드 중복성을 크게 줄입니다. 기본 구현은 다양한 응답 유형 단축키를 제공합니다. Illuminate \ support \ Facades \ http를 사용하십시오. http :: 가짜 ([ 'google.com'=> 'Hello World', 'github.com'=> [ 'foo'=> 'bar'], 'forge.laravel.com'=>

Alipay PHP ...

고객의 가장 긴급한 문제에 실시간 인스턴트 솔루션을 제공하고 싶습니까? 라이브 채팅을 통해 고객과 실시간 대화를 나누고 문제를 즉시 해결할 수 있습니다. 그것은 당신이 당신의 관습에 더 빠른 서비스를 제공 할 수 있도록합니다.

기사는 PHP 5.3에 도입 된 PHP의 LSB (Late STATIC BING)에 대해 논의하여 정적 방법의 런타임 해상도가보다 유연한 상속을 요구할 수있게한다. LSB의 실제 응용 프로그램 및 잠재적 성능

이 기사에서는 프레임 워크에 사용자 정의 기능 추가, 아키텍처 이해, 확장 지점 식별 및 통합 및 디버깅을위한 모범 사례에 중점을 둡니다.

PHP 개발에서 PHP의 CURL 라이브러리를 사용하여 JSON 데이터를 보내면 종종 외부 API와 상호 작용해야합니다. 일반적인 방법 중 하나는 컬 라이브러리를 사용하여 게시물을 보내는 것입니다 ...
