【问题标题】:Should functions like this have Unit Tests?像这样的功能应该有单元测试吗?
【发布时间】:2012-08-07 07:33:43
【问题描述】:

我已经使用 PHP 和 JS 编写了一个现在可以运行的 web 应用程序,并决定我应该在清理代码的同时学习单元测试并立即实施它。

我对应该进行单元测试的内容感到困惑。我见过的每个 PHPUnit 教程都测试了 getter、setter、对数组中的项目进行计数。我正在处理的网站上有一个显示照片的页面。用户可以喜欢照片或将其添加到他的收藏夹。 PHP 主要在站点中作为 API 层用于客户端运行的主干.js。

我应该如何为这些函数编写单元测试?一个函数(如下所示)获取通过 AJAX 发送给它的 $_GET 数据,并将一些行插入到数据库中。它不包含任何 setter、getter 或计数任何东西,也不是一个类。它甚至应该进行单元测试吗?

我可以为这些函数编写的单元测试示例将非常棒! :)

/**
 * Create new Set and add item to it
 *  @return  void
 */ 
public function action_create_set() {
    // Get data from user
    $user_id = Input::get('user_id');
    $post_id = Input::get('post_id');
    $set_name = Input::get('set_name');

    // Create new set
    $data = array(
        'user_id' => $user_id,
        'name' => $set_name
    );
    $set_id = DB::table('sets')->insert_get_id($data);

    // Add item to newly created set
    DB::query("INSERT IGNORE INTO posts_sets (post_id, set_id, user_id)
        VALUES ($post_id, $set_id, $user_id)");

    // Change `created_at` & `updated_at` col of 'sets'
    $data = array(
        'created_at' => DB::raw('NOW()'),
        'updated_at' => DB::raw('NOW()')
        );
    DB::table('sets')
        ->where('id', '=', $set_id)
        ->update($data);
}

对于第一个函数,我的印象是可以编写一个测试来检查这 3 个变量 $user_id, $post_id, $set_name 是否必须包含数据。我觉得应该有一个测试来检查 2 个insert 查询是否有效,但我也认为插入行的函数是由 PHP 框架提供的,并且已经过彻底测试,因此不需要进一步的单元测试。

另一个猜测是测试应该给函数提供3个变量,然后检查新行是否已经插入到这2个表中,但这不会被认为是集成测试吗?


这是一个函数,它通过 AJAX 从用户那里获取输入,然后以 JSON 格式返回结果。 PHPUnit 应该处理这种 API 函数吗?还是应该在客户端进行单元测试?

/**
 * Get items Liked by user
 * @return array
 */
public function action_likes() {

    $user_id = Input::get('user_id');

    $likes = DB::table('likes')
                ->join('posts', 'posts.id', '=', 'likes.post_id')
                ->where('likes.user_id', '=', $user_id)
                ->get();

    return json_encode($likes);

}

【问题讨论】:

    标签: php unit-testing tdd phpunit


    【解决方案1】:

    首先,您应该制作一个“单元”进行测试。

    您当前的实现将所有内容都放入一个函数中。这样的代码很难测试。

    首先,我建议将它分成 3 个部分。

    第一部分处理$_GET并组成一个内部表达式。 然后第二部分将其保存到数据库中。 最后,第三部分接收数据库中的对象,并呈现响应。

    如果你把它分开,第二个和第三个函数将变成可测试的。

    这太天真了,但也许值得作为你的第一步。如果你有兴趣,请搜索MVC。

    【讨论】:

      【解决方案2】:

      另一个猜测是测试应该提供 3 个变量给 函数,然后检查新行是否已插入 2 个表,但这不会被视为集成测试吗?

      一般说明:当您开始学习一般的单元测试和测试自动化时,您会很快意识到像“单元测试”和“集成测试”这样的术语实际上是相对的,并且非常依赖于上下文。

      根据您对 action_create_set 函数的描述,您似乎可以将其视为 Web api 层的一部分。如果您决定为此层编写测试,那么是的,测试此功能是否真的以正确的方式转换您的数据库是一个好主意。

      是单元测试吗?好吧,有人可能会争辩说答案是否定的,因为这个函数做了“太多”的事情(从 HTTP 请求转换数据,进行数据库调用,如果你通过发出虚假的 http 请求来测试它,这通常涉及你的整个 http 处理堆栈)。

      另一方面,您正在测试 Web api 的一个功能,该功能在应用程序的业务逻辑方面具有一个明确定义的单一职责。这将是肯定答案的论据。

      我个人更喜欢后者,但归根结底,这并不重要。

      这是一个函数,它通过 AJAX 从用户那里获取输入,然后返回 JSON 格式的结果。 PHPUnit 是否应该处理这种 API 职能?还是应该在客户端进行单元测试?

      视情况而定。从测试自动化纯粹主义者的角度来看,完美的解决方案是在双方都进行测试。

      服务器端测试应该描述和测试你的 action_likes 函数在不同情况下的表现:

      • 当没有数据可用时是否返回空集?
      • 如果 user_id 不存在或格式不正确会怎样?
      • 典型场景返回什么?

      每个测试都应该从数据库中的一些预设数据开始,并检查返回的 JSON 的内容。这样你就可以测试你的服务器 API 层了。

      但测试客户端代码也很有意义。大多数情况下,从 ajax 调用成功接收数据应该会产生一些副作用。

      例如,您可以测试是否实际调用了应该处理接收到的数据的适当回调,或者某些数据结构会做出相应的反应。

      在这种测试中,您不是与真实服务器交互,而是使用为此目的设计的 js 库来模拟 AJAX 请求和响应。其中有很多。我目前的偏好包括buster.js,我发现它也非常适合在持续集成环境中使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-02
        • 2011-01-29
        • 1970-01-01
        • 2013-03-18
        • 2015-12-27
        • 1970-01-01
        • 2019-01-12
        • 1970-01-01
        相关资源
        最近更新 更多