
PHP ์จ๋ผ์ธ ๊ฐ์
>PHP - ์ต์๊ธ
๐ PHP ์ต์๊ธ - 6์ฃผ์ฐจ: ํ ์คํธ ์๋ํ (๋จ์/ํตํฉ/E2E ํ ์คํธ)- 02 ํตํฉ ํ ์คํธ ์์ฑ

์ฃผ์ ํ๋ก๊ทธ๋จ ์คํ
- ํ์ : 10.0
- ๋ผ์ด์ ์ค: free
- ์ด์์ฒด์ :
- ํ์ผ ํฌ๊ธฐ: 0
ํผ๋๋ฐฑ ๋ฐ ๋ค์ด๋ก๋
- ์ฌ์ฉ์ ํ์ : 10.0
- ๋ค์ด๋ก๋ ์: 0
- ์กฐํ์: 1
์ ์กฐ์ฌ ๋ฐ ๋ฑ๋ก ์ ๋ณด
- ์ ์์ฌ: LUZENSOFT
- ๋ฑ๋ก์ผ: 2025-10-20 13:28:58
- ์ค๋ช
ํตํฉ ํ ์คํธ์ ์ดํด์ ์ค์์ฑ
#์ํํธ์จ์ด #๊ฐ๋ฐ์์ #ํ ์คํธ๋ ์ฌ๋ฌ ๊ณ์ธต์ผ๋ก ๋๋ ์ ์์ผ๋ฉฐ, ์ง๋๋ฒ #๋จ์ #ํ ์คํธ(#Unit_Test)์ ์ด์ด ์ด๋ฒ์๋ **#ํตํฉ #ํ ์คํธ(#Integration_Test)**์ ๋ํด ๋ค๋ฃฐ ๊ฑฐ์์. #ํตํฉ #ํ ์คํธ๋ ์ฌ๋ฌ #๋ชจ๋(#Module)์ด๋ #์ปดํฌ๋ํธ(#Component)๊ฐ ์๋ก ์ด๋ป๊ฒ ์ํธ์์ฉํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ค์ด ํจ๊ป ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋์ง ๊ฒ์ฆํ๋ ๊ณผ์ ์ด์์.
#๋จ์ #ํ ์คํธ๊ฐ ๊ฐ๋ณ ์ฝ๋์ ์ ํ์ฑ์ ๋ณด์ฅํ๋ค๋ฉด, #ํตํฉ #ํ ์คํธ๋ ์ด๋ค์ด ๊ฒฐํฉ๋์์ ๋ ๋ฐ์ํ ์ ์๋ ๋ฌธ์ , ์๋ฅผ ๋ค์ด #์ธํฐํ์ด์ค(#Interface) ๋ถ์ผ์น, #๋ฐ์ดํฐ #์ ๋ฌ ์ค๋ฅ ๋ฑ์ ์ฐพ์๋ด์. ์ด๋ ์ค์ ์ฌ์ฉ์ ์๋๋ฆฌ์ค์ ๋ ๊ฐ๊น์ด ํ๊ฒฝ์์ ํ ์คํธํจ์ผ๋ก์จ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฌ๊ณ ์ฑ์ ํ๋ณดํ๋ ๋ฐ ๋งค์ฐ ์ค์ํด์.
ํตํฉ ํ ์คํธ ํ๊ฒฝ ์ค์
#PHP์์ #ํตํฉ #ํ ์คํธ๋ฅผ ์์ฑํ ๋๋ #PHPUnit์ ์ฃผ๋ก ์ฌ์ฉํ์ง๋ง, ์ค์ #๋ฐ์ดํฐ๋ฒ ์ด์ค๋ #์ธ๋ถ #์๋น์ค์์ ์ฐ๋์ด ํ์ํ ์ ์์ผ๋ฏ๋ก ๋ช ๊ฐ์ง ์ถ๊ฐ์ ์ธ ์ค์ ์ด ํ์ํ ์ ์์ด์.
1. ํ ์คํธ ์ ์ฉ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐ ํ๊ฒฝ
๋ฐ์ดํฐ๋ฒ ์ด์ค: #ํตํฉ #ํ ์คํธ๋ ์ค์ #DB์ ์ ๊ทผํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ผ๋ฏ๋ก, ๊ฐ๋ฐ์ฉ #DB์๋ ๋ณ๊ฐ์ ํ ์คํธ ์ ์ฉ #๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์์. ๊ฐ ํ ์คํธ ์คํ ์ ์ #DB๋ฅผ ์ด๊ธฐํ(#Migration, #Seeding)ํ์ฌ ์ผ๊ด๋ ์ํ๋ฅผ ์ ์งํ๋ ๊ฒ์ด ์ค์ํด์.
ํ๊ฒฝ ๋ณ์: #PHPUnit ์ค์ ํ์ผ(
phpunit.xml
)์์ #ํ ์คํธ ํ๊ฒฝ์ ๋ง๋ #ํ๊ฒฝ #๋ณ์(#Environment_Variable)๋ฅผ ์ค์ ํ๊ฑฐ๋, ๋ณ๋์ ์ค์ ํ์ผ์ ๋ก๋ํ๋๋ก ํ ์ ์์ด์.
2. ํ ์คํธ ํฌํผ ๋ฐ ๋ถํธ์คํธ๋ฉ ํ์ผ
phpunit.xml
์ ์ง์ ๋bootstrap
ํ์ผ์์ #ํ ์คํธ #ํ๊ฒฝ์ ์ด๊ธฐํํ๋ ๋ก์ง์ ๊ตฌํํด์. ์๋ฅผ ๋ค์ด, #DI #์ปจํ ์ด๋(#Dependency_Injection_Container) ์ค์ , #DB #์ฐ๊ฒฐ, #ORM ์ด๊ธฐํ ๋ฑ์ ์ํํด์.tests/bootstrap.php
์์:PHP
<?php require dirname(__DIR__) . '/vendor/autoload.php'; // ํ ์คํธ ํ๊ฒฝ ๋ก๋ (์: .env.testing) if (file_exists(dirname(__DIR__) . '/.env.testing')) { $dotenv = DotenvDotenv::createImmutable(dirname(__DIR__), '.env.testing'); $dotenv->load(); } // ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ค์ (์: PDO) // $pdo = new PDO(...) // DB ๋ง์ด๊ทธ๋ ์ด์ ๋ฐ ์๋ฉ // ...
ํตํฉ ํ ์คํธ ์์ฑ ์์น ๋ฐ ์ค์ ์์
#๋จ์ #ํ
์คํธ์ ๋ง์ฐฌ๊ฐ์ง๋ก #PHPUnit์ TestCase
๋ฅผ ์์๋ฐ์ง๋ง, ํ
์คํธ์ ์ด์ ์ด ๋ฌ๋ผ์ ธ์.
1. ํ ์คํธ ๋์
#์ปจํธ๋กค๋ฌ(#Controller)์ #์๋น์ค(#Service), #์๋น์ค์ #๋ฆฌํฌ์งํ ๋ฆฌ(#Repository), #๋ฆฌํฌ์งํ ๋ฆฌ์ #DB ๋ฑ ์ฌ๋ฌ ๊ณ์ธต์ด ํจ๊ป ๋์ํ๋ ์๋๋ฆฌ์ค๋ฅผ ํ ์คํธํด์.
์ธ๋ถ #API์์ ์ฐ๋, #ํ์ผ #์์คํ (#File_System) ์ ๊ทผ ๋ฑ๋ ํฌํจ๋ ์ ์์ด์.
2. Fixtures (ํ ์คํธ ๊ณ ์ ๊ฐ)
#ํตํฉ #ํ ์คํธ๋ ์ข ์ข ํน์ #์ด๊ธฐ #๋ฐ์ดํฐ #์ํ๋ฅผ ์๊ตฌํด์. ์ด๋ฅผ ์ํด #Fixtures๋ฅผ ์ฌ์ฉํ์ฌ ํ ์คํธ ์คํ ์ ์ #DB์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ฃ์ด๋๊ณ , ํ ์คํธ ์๋ฃ ํ ์ ๋ฆฌํ๋ ๊ณผ์ ์ ๊ฑฐ์ณ์.
setUp()
๋ฉ์๋์์ #Fixtures๋ฅผ ๋ก๋ํ๊ณ ,tearDown()
๋ฉ์๋์์ ์ ๋ฆฌํด์.์์: ๊ฒ์ํ #์๋น์ค์ ๊ธ ์์ฑ ๋ฐ ์กฐํ ํ ์คํธ
3. ์ค์ ์์: ๊ฒ์๋ฌผ ๊ด๋ฆฌ ํตํฉ ํ ์คํธ
๊ฐ๋จํ ๊ฒ์๋ฌผ ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ๊ฐ์ ํ๊ณ , ๊ฒ์๋ฌผ์ ์์ฑํ๊ณ ์กฐํํ๋ ์๋๋ฆฌ์ค์ ๋ํ ํตํฉ ํ ์คํธ๋ฅผ ์์ฑํด ๋ณผ๊ฒ์.
src/PostRepository.php
PHP
<?php
// PostRepository.php (๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ๋)
class PostRepository {
private PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function create(string $title, string $content): int {
$stmt = $this->pdo->prepare("INSERT INTO posts (title, content) VALUES (:title, :content)");
$stmt->execute([':title' => $title, ':content' => $content]);
return (int)$this->pdo->lastInsertId();
}
public function find(int $id): ?array {
$stmt = $this->pdo->prepare("SELECT * FROM posts WHERE id = :id");
$stmt->execute([':id' => $id]);
$post = $stmt->fetch(PDO::FETCH_ASSOC);
return $post ?: null;
}
}
tests/Integration/PostManagementTest.php
PHP
<?php
use PHPUnitFrameworkTestCase;
// ํ
์คํธ ์ ์ฉ PDO ๊ฐ์ฒด (์ค์ DB ์ฐ๊ฒฐ)
class TestDatabase
{
private static ?PDO $pdo = null;
public static function getConnection(): PDO
{
if (self::$pdo === null) {
// .env.testing ๋ฑ์์ DB_DSN, DB_USER, DB_PASS ๋ถ๋ฌ์ค๊ธฐ
$dsn = $_ENV['DB_DSN'] ?? 'sqlite::memory:';
$user = $_ENV['DB_USER'] ?? null;
$pass = $_ENV['DB_PASS'] ?? null;
self::$pdo = new PDO($dsn, $user, $pass);
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$pdo->exec("CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL
)");
}
return self::$pdo;
}
public static function resetDatabase(): void
{
$pdo = self::getConnection();
$pdo->exec("DELETE FROM posts");
$pdo->exec("DELETE FROM sqlite_sequence WHERE name='posts'"); // SQLite AUTOINCREMENT ์ฌ์ค์
}
}
class PostManagementTest extends TestCase
{
private PostRepository $postRepository;
protected function setUp(): void
{
parent::setUp();
TestDatabase::resetDatabase(); // ๊ฐ ํ
์คํธ ์ ์ DB ์ด๊ธฐํ
$this->postRepository = new PostRepository(TestDatabase::getConnection());
}
public function testPostCreationAndRetrieval(): void
{
// Arrange
$title = "์ฒซ ๋ฒ์งธ ๊ฒ์๊ธ";
$content = "๋ด์ฉ์
๋๋ค.";
// Act - ๊ฒ์๋ฌผ ์์ฑ ๋ฐ ์กฐํ (PostRepository์ PDO๊ฐ ํจ๊ป ์๋)
$newPostId = $this->postRepository->create($title, $content);
$retrievedPost = $this->postRepository->find($newPostId);
// Assert
$this->assertNotNull($retrievedPost);
$this->assertEquals($title, $retrievedPost['title']);
$this->assertEquals($content, $retrievedPost['content']);
$this->assertEquals($newPostId, $retrievedPost['id']);
}
public function testNonExistentPostRetrieval(): void
{
// Act
$retrievedPost = $this->postRepository->find(999); // ์กด์ฌํ์ง ์๋ ID
// Assert
$this->assertNull($retrievedPost);
}
}
์ ์์๋ PostRepository
์ ์ค์ #PDO #๋ฐ์ดํฐ๋ฒ ์ด์ค #์ฐ๊ฒฐ์ด ํจ๊ป ๋์ํ๋์ง ํ์ธํ๋ ํตํฉ ํ
์คํธ์์. TestDatabase
ํฌํผ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ #ํ
์คํธ #์ ์ฉ #DB๋ฅผ ์ค์ ํ๊ณ ๋งค ํ
์คํธ๋ง๋ค ์ด๊ธฐํํ๋ ๊ฒ์ ๋ณผ ์ ์์ด์.
์์ฝ
#ํตํฉ #ํ ์คํธ๋ ๊ฐ๋ณ ๋จ์๊ฐ ๊ฒฐํฉ๋์ด ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํ๋์ง ํ์ธํ์ฌ, ์ค์ #์ ํ๋ฆฌ์ผ์ด์ ์ ๋์๊ณผ ๊ฐ์ฅ ์ ์ฌํ ํ๊ฒฝ์์ ๋ฌธ์ ๋ฅผ ์ฐพ์๋ด๋ ๋ฐ ์ค์ํ ์ญํ ์ ํด์. #PHPUnit๊ณผ ์ ์ ํ #ํ๊ฒฝ #์ค์ , ๊ทธ๋ฆฌ๊ณ #Fixtures ๊ด๋ฆฌ๋ฅผ ํตํด ํจ๊ณผ์ ์ธ #ํตํฉ #ํ ์คํธ๋ฅผ ์์ฑํจ์ผ๋ก์จ #PHP #์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ขฐ๋๋ฅผ ํฌ๊ฒ ๋์ผ ์ ์์ด์.
๋น ๋ฅธ์๋, ๊ฐํธํ์ฌ์ฉ, ์ฅ์ ์๋VPN, ์ฌ์ฉ์ด๋ ฅ์๋ ๊นจ๋ํ ์์ดํผ