2012-01-28 31 views
5

Tôi có thể điền php://input bằng dữ liệu nhị phân để kiểm tra tải lên bằng cách nào? (Hoặc nếu không hãy thử tải lên chunked). Tôi đang sử dụng plupload làm lối vào của mình và tôi muốn hủy phụ trợ của mình.PHPUnit kiểm tra dữ liệu nhị phân tải lên

Đây là đoạn mã tôi muốn thử nghiệm:

public function recieve($file = 'file') 
{ 
    // Get parameters 
    $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0; 
    $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0; 
    $fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : ''; 
    $targetDir = $this->_uploadDir; 

    // Clean the fileName for security reasons 
    $fileName = preg_replace('/[^\w\._]+/', '_', $fileName); 

    // Make sure the fileName is unique but only if chunking is disabled 
    if ($chunks < 2 && file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName)) { 
     $ext = strrpos($fileName, '.'); 
     $fileName_a = substr($fileName, 0, $ext); 
     $fileName_b = substr($fileName, $ext); 

     $count = 1; 
     while (file_exists(
       $targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b)) { 
      $count++; 
     } 

     $fileName = $fileName_a . '_' . $count . $fileName_b; 
    } 

    $filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName; 

    // Create target dir 
    if (!file_exists($targetDir)) { 
     if (!is_writable(dirname($targetDir))) { 
      $this->_messages[] = 'Cannot write to ' . dirname($targetDir) . ' for mkdir'; 
      return false; 
     } 
     mkdir($targetDir, 0777, true); 
    } 

    // Check permissions 
    if (!is_writable($targetDir)) { 
     $this->_messages[] = 'Unable to write to temp directory.'; 
     return false; 
    } 

    // Look for the content type header 
    $contentType = null; 
    if (isset($_SERVER["HTTP_CONTENT_TYPE"])) 
     $contentType = $_SERVER["HTTP_CONTENT_TYPE"]; 

    if (isset($_SERVER["CONTENT_TYPE"])) 
     $contentType = $_SERVER["CONTENT_TYPE"]; 

    // Handle non multipart uploads older WebKit versions didn't support multipart in HTML5 
    if (strpos($contentType, "multipart") !== false) { 
     if (isset($_FILES[$file]['tmp_name']) && is_uploaded_file($_FILES[$file]['tmp_name'])) { 
      // Open temp file 
      $out = fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab"); 
      if ($out) { 
       // Read binary input stream and append it to temp file 
       $in = fopen($_FILES[$file]['tmp_name'], "rb"); 

       if ($in) { 
        while ($buff = fread($in, 4096)) { 
         fwrite($out, $buff); 
        } 
       } else { 
        $this->_messages[] = 'Failed to open input stream.'; 
        return false; 
       } 
       fclose($in); 
       fclose($out); 
       unlink($_FILES[$file]['tmp_name']); 
      } else { 
       $this->_messages[] = 'Failed to open output stream.'; 
       return false; 
      } 
     } else { 
      $this->_messages[] = 'Failed to move uploaded file.'; 
      return false; 
     } 
    } else { 
     // Open temp file 
     $out = fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab"); 
     if ($out) { 
      // Read binary input stream and append it to temp file 
      $in = fopen("php://input", "rb"); 
      if ($in) { 
       while ($buff = fread($in, 4096)) { 
        fwrite($out, $buff); 
       } 
      } else { 
       $this->_messages[] = 'Failed to open input stream.'; 
       return false; 
      } 
      fclose($in); 
      fclose($out); 
     } else { 
      $this->_messages[] = 'Failed to open output stream.'; 
      return false; 
     } 
    } 

    // Check if file upload is complete 
    if (!$chunks || $chunk == $chunks - 1) { 
     // Strip the temp .part suffix off 
     rename("{$filePath}.part", $filePath); 
     return $filePath; 
    } 
} 

* Sửa:

Nhập mã hơn, để hiển thị những gì tôi muốn kiểm tra đơn vị

+0

Bạn có muốn ** đơn vị ** hoặc kiểm tra tích hợp này không? Nếu bạn muốn tích hợp kiểm tra nó: Để những gì mở rộng? Bao gồm cả đi qua máy chủ web hay không ?. $ _REQUEST dường như đề xuất một bài kiểm tra tích hợp nhưng tôi muốn hỏi trước khi trả lời. – edorian

+0

Tôi muốn đơn vị kiểm tra mã máy chủ nhận tải lên, để xác minh rằng bất kỳ tệp tải lên nào sẽ được xử lý như mong đợi –

+0

Xin lỗi vì đã hỏi lại nhưng tôi không theo dõi. Bạn có thể ether UNIT kiểm tra đoạn mã này hoặc kiểm tra tệp tải lên thông qua máy chủ web, cả hai thứ đều không có ý nghĩa ngữ nghĩa. Nếu bạn muốn tải lên tệp "giả mạo" là bài kiểm tra tích hợp. Có lẽ chỉ là những từ là vấn đề ở đây? http: // stackoverflow.com/questions/516572/am-i-unit-testing-or-integration-testing – edorian

Trả lời

6

Có vẻ điều này không thể được thực hiện với các bài kiểm tra PHPUnit thường xuyên, nhưng tôi tìm thấy một cách để tích hợp kiểm tra .phpt với PHPUnit tại địa chỉ: http://qafoo.com/blog/013_testing_file_uploads_with_php.html

Để tham khảo, uploadTest.phpt:

--TEST-- 
Example test emulating a file upload 
--POST_RAW-- 
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryfywL8UCjFtqUBTQn 

------WebKitFormBoundaryfywL8UCjFtqUBTQn 
Content-Disposition: form-data; name="file"; filename="example.txt" 
Content-Type: text/plain 

Contents of text file here 

------WebKitFormBoundaryfywL8UCjFtqUBTQn 
Content-Disposition: form-data; name="submit" 

Upload 
------WebKitFormBoundaryfywL8UCjFtqUBTQn-- 
--FILE-- 
<?php 
require __DIR__ . '/Upload.php'; 

$upload = new Upload(); 
$file = $upload->recieve('file'); 

var_dump(file_exists($file)); 
?> 
--EXPECT-- 
bool(true) 

Và tương thích thử nghiệm PHPUnit tương ứng:

<?php 
require_once 'PHPUnit/Extensions/PhptTestCase.php'; 
class UploadExampleTest extends PHPUnit_Extensions_PhptTestCase 
{ 
    public function __construct() 
    { 
     parent::__construct(__DIR__ . '/uploadTest.phpt'); 
    } 
} 
+0

Điều này là hoàn hảo cho một bài kiểm tra tích hợp. –

+0

+1, giải pháp tốt đẹp. – hakre

+0

Có vẻ như không làm cho nó hoạt động. Trước hết * thực thi kiểm tra .phpt cần tùy chọn '--cgi', do sử dụng' GET' hoặc 'POST' trong thử nghiệm. Ngoài ra tài liệu. nhà nước, rằng 'Hiện chỉ có sẵn với máy chủ-tests.php' và tôi không có ý tưởng nơi để tìm nó và làm thế nào nó được kết nối với thử nghiệm của tôi. – Eugene

2

Trước tiên, bạn 'd tìm mã này dễ dàng hơn đáng kể để kiểm tra đơn vị nếu nó không phải là một phương pháp 200 dòng duy nhất! Đơn vị càng nhỏ - thử nghiệm càng nhỏ. Bạn có thể trích xuất getFileName(), getContentType(), isChunked() hoặc getChunkDetails(), transferChunk(), v.v. Nhiều phương pháp này sẽ rất ngắn và cho phép bạn kiểm tra kỹ lưỡng mà không phải thiết lập toàn bộ quá trình tải lên. Dưới đây là một ví dụ, getContentType():

public function getContentType() { 
    if (isset($_SERVER["CONTENT_TYPE"])) 
     return $_SERVER["CONTENT_TYPE"]; 

    if (isset($_SERVER["HTTP_CONTENT_TYPE"])) 
     return $_SERVER["HTTP_CONTENT_TYPE"]; 

    throw new FileTransferException('Unknown content type'); 
} 

Các thử nghiệm cho phương pháp này là thẳng về phía trước.

/** 
* @expectedException FileTransferException 
*/ 
public function testUnknownContentType() { 
    $fixture = new FileTransfer(); 
    unset($_SERVER["CONTENT_TYPE"]); 
    unset($_SERVER["HTTP_CONTENT_TYPE"]); 
    $fixture->getContentType(); 
} 

public function testRegularContentType() { 
    $fixture = new FileTransfer(); 
    $_SERVER["CONTENT_TYPE"] = 'regular'; 
    unset($_SERVER["HTTP_CONTENT_TYPE"]); 
    self::assertEquals('regular', $fixture->getContentType()); 
} 

public function testHttpContentType() { 
    $fixture = new FileTransfer(); 
    unset($_SERVER["CONTENT_TYPE"]); 
    $_SERVER["HTTP_CONTENT_TYPE"] = 'http'; 
    self::assertEquals('http', $fixture->getContentType()); 
} 

public function testRegularContentTypeTakesPrecedence() { 
    $fixture = new FileTransfer(); 
    $_SERVER["HTTP_CONTENT_TYPE"] = 'http'; 
    $_SERVER["CONTENT_TYPE"] = 'regular'; 
    self::assertEquals('regular', $fixture->getContentType()); 
} 

Khi bạn đã refactored mã với những thứ dễ dàng, bạn có thể trích xuất tất cả các I/O xử lý vào một lớp riêng biệt. Bằng cách đó, bạn có thể sử dụng đối tượng giả khi kiểm tra mã không phải là I/O, có nghĩa là bạn sẽ không phải dựa vào các tệp thực hoặc nhồi nhét php://input với dữ liệu giả mạo. Đây là phần "đơn vị" của "kiểm tra đơn vị": chia nhỏ mã của bạn thành các đơn vị nhỏ, có thể kiểm tra và loại bỏ các đơn vị khác khỏi phương trình trong đó thực tế.

Trong lớp xử lý I/O đã giải nén, hãy đặt các cuộc gọi đến is_uploaded_file() và mở luồng đầu vào thành các phương thức riêng biệt, ví dụ: isUploadedFile()openInputStream(). Trong khi thử nghiệm, bạn có thể thử các phương pháp đó thay vì chế nhạo các cơ chế cơ bản của chúng. Không có điểm nào trong thử nghiệm rằng is_uploaded_file() hoạt động trong một thử nghiệm đơn vị. Đó là trách nhiệm của PHP và bạn có thể xác minh mọi thứ hoạt động như mong đợi trong thử nghiệm tích hợp (end-to-end).

Điều này sẽ giảm kiểm tra mã I/O của bạn xuống mức tối thiểu. Tại thời điểm đó, bạn có thể sử dụng các tệp thực trong thư mục kiểm tra của mình hoặc gói như vfsStream.

+0

Trong khi tôi đồng ý với các điểm của bạn, đó là lời khuyên tốt nói chung, họ không giải quyết câu hỏi thực tế của tôi là về tải lên tệp thử nghiệm đơn vị. Cụ thể là 'is_uploaded_file' và đầu vào php: // sẽ không thành công trong mã của tôi. Có vẻ như với tôi cách duy nhất để kiểm tra điều này là sử dụng các tệp .phpt, vì is_uploaded_file và php: // input không thể được mô phỏng theo như tôi đã tìm ra. –

+0

Tôi đã thêm đoạn thứ hai đến cuối để hy vọng làm rõ một chút. Vấn đề là bạn sẽ không gọi 'is_uploaded_file()' hoặc sử dụng 'php: // input'. Thay vào đó, hãy thử phương thức 'isUploadedFile()' và 'openInputStream()' của riêng bạn. –

+0

Bạn không nên thay đổi triển khai để giải quyết vấn đề kiểm tra đơn vị theo ý kiến ​​của tôi. :) –

Các vấn đề liên quan