如何使用PHPUnit与CodeIgniter? [英] How do I use PHPUnit with CodeIgniter?

查看:191
本文介绍了如何使用PHPUnit与CodeIgniter?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读和阅读关于PHPUnit,SimpleTest和其他单元测试框架的文章。他们都听起来这么伟大!我终于得到PHPUnit使用Codeigniter感谢 https://bitbucket.org/kenjis/my-ciunit/overview

I have read and read articles on PHPUnit, SimpleTest, and other Unit Testing frameworks. They all sound so great! I finally got PHPUnit working with Codeigniter thanks to https://bitbucket.org/kenjis/my-ciunit/overview

现在我的问题是,如何使用它?

Now my question is, how do I use it?

我看到的每个教程都有一些抽象的使用 assertEquals(2,1 + 1)或:

Every tutorial I see has some abstract use like assertEquals(2, 1+1) or:

public function testSpeakWithParams()
{
    $hello = new SayHello('Marco');
    $this->assertEquals("Hello Marco!", $hello->speak());
}

这是伟大的,如果我有一个函数,将输出这样一个可预测的字符串。通常我的应用程序从数据库获取一堆数据,然后在某种表中显示它。那么如何测试Codeigniter的控制器?

That is great if I had a function that would output such a predictable string. Usually my apps grab a bunch of data from the database then display it in some sort of table. So how do I test Codeigniter's controllers?

我想做测试驱动开发,我已经阅读了PHPUnits网站上的教程,但是这个例子似乎是抽象的。

I would like to do Test-Driven Development and I have read the tutorial on PHPUnits site, but once again the example seem so abstract. Most of my codeigniter functions are displaying data.

有实用的应用程序和PHPUnit测试的例子吗?

Is there a book or a great tutorial with a practical application and examples of PHPUnit testing?

推荐答案

看来你理解如何编写测试和单元测试的基本结构/语法CodeIgniter代码不应该与测试非CI代码有任何不同,因此我想关注你的根本问题/问题...

It seems you understand the basic structure/syntax of how to write tests and unit testing CodeIgniter code shouldn't be any different from testing non-CI code, so I want to focus on your underlying concerns/issues ...

我有不同的问题不久以前与PHPUnit。作为没有正式训练的人,我发现进入单元测试心态首先似乎抽象和不自然。我认为这个的主要原因 - 在我的情况下,也可能是你的问题,是你没有专注于真的工作,以分离你的代码中的问题,直到现在。

I had similar questions not too long ago with PHPUnit. As someone without formal training I found that getting into the Unit Testing mindset seemed abstract and unnatural at first. I think the main reason for this -- in my case, and probably yours too from the question -- is that you haven't focused on REALLY working to separate the concerns in your code up till now.

测试断言看起来很抽象,因为大多数方法/函数可能执行几个不同的离散任务。成功的测试心态需要改变你对代码的看法。你应该停止定义成功的它工作?相反,你应该问,它工作,它将与其他代码,它的设计方式,使其在其他应用程序中有用,我可以验证它的工作原理吗?

The testing assertions seem abstract because most of your methods/functions likely perform several different discrete tasks. A successful testing mentality requires a change in how you think about your code. You should stop defining success in terms of "does it work?" Instead you should ask, "does it work, will it play well with other code, is it designed in a way that makes it useful in other applications and can I verify that it works?"

例如,下面是一个简单的例子,说明你可能如何编写代码到这一点:

For example, below is a simplified example of how you've probably written code up to this point:

function parse_remote_page_txt($type = 'index')
{
  $remote_file = ConfigSingleton::$config_remote_site . "$type.php";
  $local_file  = ConfigSingleton::$config_save_path;

  if ($txt = file_get_contents($remote_file)) {
    if ($values_i_want_to_save = preg_match('//', $text)) {
      if (file_exists($local_file)) {
        $fh = fopen($local_file, 'w+');
        fwrite($fh, $values_i_want_to_save);
        fclose($fh);
        return TRUE;
      } else {
        return FALSE;
      }
  } else {
    return FALSE;
  }  
}

这里发生的事情并不重要。我试图说明为什么这个代码很难测试:

Exactly what's going on here isn't important. I'm trying to illustrate why this code is difficult to test:


  • 它使用单例配置类来生成值。你的函数的成功取决于单例的值,当你不能用不同的值实例化新的配置对象时,如何测试这个函数在完全隔离中正确工作?一个更好的选择可能是传递你的函数a $ config 参数由一个配置对象或数组,其值可以控制。这被广泛地称为依赖注入,并且有关于该技术的讨论

  • It's using a singleton configuration class to generate values. The success of your function depends on values from the singleton, and how can you test that this function works correctly in complete isolation when you can't instantiate new config objects with different values? A better option might be to pass your function a $config argument that consists of a config object or array whose values you can control. This is broadly termed "Dependency Injection" and there are discussions of this technique all over the interwebs.

请注意嵌套的 IF 语句。测试意味着你用某种测试覆盖每个可执行行。

Notice the nested IF statements. Testing means you're covering every executable line with some sort of test. When you nest IF statements you're creating new branches of code that require a new test path.

最后,你看看这个函数,虽然看起来像这样做一件事(解析远程文件的内容)实际上正在执行几个任务?如果你热心地分离你的关注,你的代码变得无限更可测试。一个更可测的方法来做同样的事情是...

Finally, do you see how this function, though it seems to be doing one thing (parsing the contents of a remote file) is actually performing several tasks? If you zealously separate your concerns your code becomes infinitely more testable. A much more testable way to do this same thing would be ...

class RemoteParser() {
  protected $local_path;
  protected $remote_path;
  protected $config;

  /**
   * Class constructor -- forces injection of $config object
   * @param ConfigObj $config
   */
  public function __construct(ConfigObj $config) {
    $this->config = $config;
  }

  /**
   * Setter for local_path property
   * @param string $filename
   */
  public function set_local_path($filename) {
    $file = filter_var($filename);
    $this->local_path = $this->config->local_path . "/$file.html";
  }

  /**
   * Setter for remote_path property
   * @param string $filename
   */
  public function set_remote_path($filename) {
    $file = filter_var($filename);
    $this->remote_path = $this->config->remote_site . "/$file.html";
  }

  /**
   * Retrieve the remote source
   * @return string Remote source text
   */
  public function get_remote_path_src() {
    if ( ! $this->remote_path) {
      throw new Exception("you didn't set the remote file yet!");
    }
    if ( ! $this->local_path) {
      throw new Exception("you didn't set the local file yet!");
    }
    if ( ! $remote_src = file_get_contents($this->remote_path)) {
      throw new Exception("we had a problem getting the remote file!");
    }

    return $remote_src;
  }

  /**
   * Parse a source string for the values we want
   * @param string $src
   * @return mixed Values array on success or bool(FALSE) on failure
   */
  public function parse_remote_src($src='') {
    $src = filter_validate($src);
    if (stristr($src, 'value_we_want_to_find')) {
      return array('val1', 'val2');
    } else {
      return FALSE;
    }
  }

  /**
   * Getter for remote file path property
   * @return string Remote path
   */
  public function get_remote_path() {
    return $this->remote_path;
  }

  /**
   * Getter for local file path property
   * @return string Local path
   */
  public function get_local_path() {
    return $this->local_path;
  }
}

正如你所看到的,该类的特定函数是容易测试的。远程文件检索工作?我们找到了我们想要解析的值吗?等等。突然,这些抽象断言看起来更有用了。

As you can see, each of these class methods handles a particular function of the class that is easily testable. Did the remote file retrieval work? Did we find the the values we were trying to parse? Etc. All of a sudden those abstract assertions seem much more useful.

IMHO,越深入测试越多,你就意识到它更好的代码设计和明智的架构而不是简单地确保事情按预期工作。这里是OOP的好处真正开始闪耀的地方。你可以测试程序代码就好了,但是对于一个大型项目,使用相互依赖的部件测试有一种强制良好设计的方法。我知道对于一些程序性的人来说可能是巨魔诱饵,但是很好。

IMHO, the more you delve into testing the more you realize it's more about good code design and sensible architecture than simply making sure things work as expected. And here is where the benefits of OOP really start to shine. You can test procedural code just fine, but for a large project with interdependent parts testing has a way of enforcing good design. I know that may be troll bait for some procedural people but oh well.

你测试得越多,你会发现自己写代码和问自己,我能测试一下吗?如果没有,你可能会改变结构,然后那里。

The more you test, the more you'll find yourself writing code and asking yourself, "Will I be able to test this?" And if not, you'll probably change the structure then and there.

然而,代码不需要是可测试的基本。 Stubbing and mocking 允许您测试外部操作的成功或失败完全出来的控制。您可以创建夹具来测试数据库操作以及其他任何内容。

However, code need not be elementary to be testable. Stubbing and mocking allows you to test external operations the success or failure of which is entirely out of control. You can create fixtures to test database operations and pretty much anything else.

我测试的越多,我越意识到,如果我有一个艰难的时间测试的东西,这很可能是因为我有一个底层的设计问题。如果我把它弄平了,通常会导致我的测试结果中的所有绿色条。

The more I test, the more I realize that if I'm having a tough time testing something it's most likely because I have an underlying design problem. If I straighten that out it usually results in all green bars in my test results.

最后,这里有几个链接,真的帮助我开始思考测试友好的时尚。第一个是一个面向对象的列表,如果你想编写可测试的代码。事实上,如果你浏览整个网站,你会发现很多有用的东西,将有助于设置你的100%的代码覆盖的路径。另一篇有用的文章是此依赖注入的讨论

Finally, here are a couple of links that really helped me to start thinking in a test-friendly fashion. The first one is a tongue-in-cheek list of what NOT to do if you want to write testable code. In fact, if you browse that entire site you'll find lots of helpful stuff that will help set you on the path to 100% code coverage. Another helpful article is this discussion of dependency injection.

祝你好运!

这篇关于如何使用PHPUnit与CodeIgniter?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆