简体   繁体   English

如何使用过程代码库在PHP中编写单元测试?

[英]How do I write unit tests in PHP with a procedural codebase?

I'm mostly convinced of the benefits of unit testing, and I would like to start applying the concept to a large existing codebase written in PHP. 我基本上相信单元测试的好处,我想开始将这个概念应用于用PHP编写的大型现有代码库。 Less than 10% of this code is object-oriented. 不到10%的代码是面向对象的。

I've looked at several unit testing frameworks (PHPUnit, SimpleTest, and phpt). 我看了几个单元测试框架(PHPUnit,SimpleTest和phpt)。 However, I haven't found examples for any of these that test procedural code. 但是,我没有找到测试过程代码的任何示例。 What's the best framework for my situation and are there any examples of unit testing PHP using non-OOP code? 什么是我的情况最好的框架,有没有使用非OOP代码单元测试PHP的例子?

You can unit-test procedural PHP, no problem. 你可以对程序PHP进行单元测试,没问题。 And you're definitely not out of luck if your code is mixed in with HTML. 如果您的代码与HTML混合在一起,那么您绝对不会运气不好。

At the application or acceptance test level, your procedural PHP probably depends on the value of the superglobals ( $_POST, $_GET, $_COOKIE , etc.) to determine behavior, and ends by including a template file and spitting out the output. 在应用程序或验收测试级别,您的过程PHP可能依赖于超级全局( $_POST, $_GET, $_COOKIE等)的值来确定行为,并以包含模板文件和吐出输出结束。

To do application-level testing, you can just set the superglobal values; 要进行应用程序级测试,您只需设置超全局值; start an output buffer (to keep a bunch of html from flooding your screen); 启动一个输出缓冲区(以防止一堆html充斥你的屏幕); call the page; 打电话给页面; assert against stuff inside the buffer; 断言缓冲区内的东西; and trash the buffer at the end. 并在最后废弃缓冲区。 So, you could do something like this: 所以,你可以这样做:

public function setUp()
{
    if (isset($_POST['foo'])) {
        unset($_POST['foo']);
    }
}

public function testSomeKindOfAcceptanceTest()
{
    $_POST['foo'] = 'bar';
    ob_start();
    include('fileToTest.php');
    $output = ob_get_flush();
    $this->assertContains($someExpectedString, $output);
}

Even for enormous "frameworks" with lots of includes, this kind of testing will tell you if you have application-level features working or not. 即使对于包含大量包含的巨大“框架”,这种测试也会告诉您是否有应用程序级功能正常工作。 This is going to be really important as you start improving your code, because even if you're convinced that the database connector still works and looks better than before, you'll want to click a button and see that, yes, you can still login and logout through the database. 当您开始改进代码时,这将非常重要,因为即使您确信数据库连接器仍然可以工作并且看起来比以前更好,您也可以单击一个按钮看看,是的,您仍然可以通过数据库登录和注销。

At lower levels, there are minor variations depending on variable scope and whether functions work by side-effects (returning true or false), or return the result directly. 在较低级别,根据变量范围以及函数是否通过副作用(返回true或false)起作用或直接返回结果,存在微小的变化。

Are variables passed around explicitly, as parameters or arrays of parameters between functions? 变量是否明确地传递,作为函数之间的参数或参数数组? Or are variables set in many different places, and passed implicitly as globals? 或者是在许多不同的地方设置变量,并作为全局变量隐式传递? If it's the (good) explicit case, you can unit test a function by (1) including the file holding the function, then (2) feeding the function test values directly, and (3) capturing the output and asserting against it. 如果它是(好的)显式情况,你可以通过(1)包括保存函数的文件,然后(2)直接输入函数测试值,以及(3)捕获输出并对其断言来单元测试函数。 If you're using globals, you just have to be extra careful (as above, in the $_POST example) to carefully null out all the globals between tests. 如果你正在使用全局变量,你只需要格外小心(如上所述,在$ _POST示例中),小心地将测试之间的所有全局变量归零。 It's also especially helpful to keep tests very small (5-10 lines, 1-2 asserts) when dealing with a function that pushes and pulls lots of globals. 在处理推动和拉动大量全局变量的函数时,保持测试非常小(5-10行,1-2断言)也特别有用。

Another basic issue is whether the functions work by returning the output, or by altering the params passed in, returning true/false instead. 另一个基本问题是函数是通过返回输出还是通过改变传入的参数来工作,而是返回true / false。 In the first case, testing is easier, but again, it's possible in both cases: 在第一种情况下,测试更容易,但同样,在两种情况下都可以:

// assuming you required the file of interest at the top of the test file
public function testShouldConcatenateTwoStringsAndReturnResult()
{
  $stringOne = 'foo';
  $stringTwo = 'bar';
  $expectedOutput = 'foobar';
  $output = myCustomCatFunction($stringOne, $stringTwo);
  $this->assertEquals($expectedOutput, $output);
}

In the bad case, where your code works by side-effects and returns true or false, you still can test pretty easily: 在坏的情况下,你的代码通过副作用工作并返回true或false,你仍然可以很容易地测试:

/* suppose your cat function stupidly 
 * overwrites the first parameter
 * with the result of concatenation, 
 * as an admittedly contrived example 
 */
public function testShouldConcatenateTwoStringsAndReturnTrue()
    {
      $stringOne = 'foo';
      $stringTwo = 'bar';
      $expectedOutput = 'foobar';
      $output = myCustomCatFunction($stringOne, $stringTwo);
      $this->assertTrue($output);
      $this->Equals($expectedOutput, $stringOne);
    }

Hope this helps. 希望这可以帮助。

What unit tests do well, and what you should use them for, is when you have a piece of code that you give some number of inputs, and you expect to get some number of outputs back out. 什么单元测试做得很好,以及你应该使用它们的是,当你有一段代码,你提供了一些输入,并且你希望得到一些输出。 The idea being, when you add functionality later, you can run your tests and make sure it's still performing the old functionality the same way. 我们的想法是,当您稍后添加功能时,您可以运行测试并确保它仍然以相同的方式执行旧功能。

So, if you have a procedural code-base, you can accomplish this calling your functions in the test methods 因此,如果您有一个过程代码库,则可以在测试方法中完成此函数调用

require 'my-libraries.php';
class SomeTest extends SomeBaseTestFromSomeFramework {
    public function testSetup() {
        $this->assertTrue(true);
    }

    public function testMyFunction() {
        $output = my_function('foo',3);

        $this->assertEquals('expected output',$output);
    }
}

This trick with PHP code bases is, often your library code will interfere with the running of your test framework, as your codebase and the test frameworks will have a lot of code related to setting up an application environment in a web browser (session, shared global variables, etc.). PHP代码库的这个技巧通常是您的库代码会干扰测试框架的运行,因为您的代码库和测试框架将有大量与在Web浏览器中设置应用程序环境相关的代码(会话,共享)全局变量等)。 Expect to spend sometime getting to a point where you can include your library code and run a dirt simple test (the testSetup function above). 期待花一些时间到达可以包含库代码并运行简单测试(上面的testSetup函数)的点。

If your code doesn't have functions, and is just a series of PHP files that output HTML pages, you're kind of out of luck. 如果你的代码没有函数,并且只是一系列输出HTML页面的PHP文件,那你就有点不走运了。 Your code can't be separated into distinct units, which means unit testing isn't going to be of much use to you. 您的代码不能分成不同的单元,这意味着单元测试对您没有多大用处。 You'd be better off spending your time at the "acceptance testing" level with products like Selenium and Watir . 使用SeleniumWatir等产品,您最好花时间在“验收测试”级别。 These will let you automated a browser, and then check the pages for content as specific locations/in forms. 这些将允许您自动化浏览器,然后检查页面中的内容作为特定位置/表单。

You could try to include your non-oop code into a test class using 您可以尝试将非oop代码包含到测试类中

require_once 'your_non_oop_file.php' # Contains fct_to_test()

And with phpUnit you define your test function : 使用phpUnit,您可以定义测试功能:

testfct_to_test() {
   assertEquals( result_expected, fct_to_test(), 'Fail with fct_to_test' );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM