如何在PHPUnit中断言两个目录完全相同?如何排查目录文件差异?
嘿,刚好我对这两个场景都很熟悉,给你详细说说~
一、找出两个目录的文件差异(缺失/多余)
最直接的方式是用系统自带的diff命令,它能帮你快速定位目录里的差异:
- 基础递归对比:在Linux/macOS终端(Windows可以用Git Bash或者WSL)执行下面的命令,
-r表示递归遍历子目录,-q只输出差异的文件/目录,不显示内容区别:
输出结果会像这样,清晰告诉你哪个目录有缺失或多余的文件:diff -rq dir1/ dir2/Only in dir1/: missing_report.pdf
Only in dir2/: extra_config.ini
Files dir1/readme.md and dir2/readme.md differ - 树形结构对比:如果想更直观看到目录层级的差异,可以先导出两个目录的树形结构再对比:
这样能一目了然看到整个目录结构的不同之处。tree dir1/ > dir1_tree.txt tree dir2/ > dir2_tree.txt diff dir1_tree.txt dir2_tree.txt
二、PHPUnit中断言两个目录完全相同的方法
PHPUnit没有内置的目录断言方法,但我们可以自己封装逻辑,或者借助常用组件来实现,这里给你两种实用方案:
方案1:手动递归对比(轻量无依赖)
自己写一个辅助函数,先对比文件列表,再逐个校验文件内容:
use PHPUnit\Framework\TestCase; class DirectoryEqualsTest extends TestCase { public function testTwoDirectoriesAreIdentical() { $targetDir = __DIR__ . '/expected'; $actualDir = __DIR__ . '/actual'; // 第一步:校验文件/子目录列表完全一致 $this->assertEquals( $this->getSortedRelativeFileList($targetDir), $this->getSortedRelativeFileList($actualDir), '目录的文件或子目录存在缺失/多余' ); // 第二步:校验每个文件的内容完全一致 foreach ($this->getSortedRelativeFileList($targetDir) as $relativePath) { $targetFile = $targetDir . '/' . $relativePath; $actualFile = $actualDir . '/' . $relativePath; // 跳过子目录,只校验文件内容 if (is_file($targetFile)) { $this->assertEquals( file_get_contents($targetFile), file_get_contents($actualFile), "文件内容不匹配:{$relativePath}" ); } } } /** * 递归获取目录下所有文件/子目录的相对路径,排序后返回 */ private function getSortedRelativeFileList(string $directory): array { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); $filePaths = []; foreach ($iterator as $item) { // 计算相对于目标目录的路径 $relativePath = str_replace(rtrim($directory, '/') . '/', '', $item->getPathname()); $filePaths[] = $relativePath; } // 排序保证对比顺序一致 sort($filePaths); return $filePaths; } }
方案2:借助Symfony Filesystem组件(更健壮)
如果你的项目已经在用Symfony的Filesystem组件,可以用哈希校验的方式,比直接读内容更高效:
use Symfony\Component\Filesystem\Filesystem; use PHPUnit\Framework\TestCase; class DirectoryComparisonTest extends TestCase { public function testDirectoriesMatchExactly() { $expectedDir = __DIR__ . '/expected'; $actualDir = __DIR__ . '/actual'; $filesystem = new Filesystem(); // 先确保两个目录都存在 $this->assertDirectoryExists($expectedDir); $this->assertDirectoryExists($actualDir); // 获取两个目录的所有文件相对路径 $expectedFiles = $this->getAllRelativeFiles($expectedDir); $actualFiles = $this->getAllRelativeFiles($actualDir); $this->assertEquals($expectedFiles, $actualFiles, '文件列表不匹配'); // 用SHA256哈希校验文件内容 foreach ($expectedFiles as $file) { $expectedHash = hash_file('sha256', $expectedDir . '/' . $file); $actualHash = hash_file('sha256', $actualDir . '/' . $file); $this->assertEquals($expectedHash, $actualHash, "文件 {$file} 内容不一致"); } } private function getAllRelativeFiles(string $directory): array { $files = []; $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS) ); foreach ($iterator as $file) { $files[] = $iterator->getSubPathName(); } sort($files); return $files; } }
如果不想自己写代码,也可以找一些PHPUnit的第三方扩展(比如phpunit-assertions),不过自己封装的逻辑更轻量,也更容易定制。
内容的提问来源于stack exchange,提问作者Tomas Votruba
相关产品推荐
相关产品推荐

