Php 单元测试模拟-对我来说没有意义

Php 单元测试模拟-对我来说没有意义,php,unit-testing,junit,mocking,phpunit,Php,Unit Testing,Junit,Mocking,Phpunit,我正在尝试学习单元测试,但遇到了以下情况,我无法理解: 我有一个模型:SalesOrder——它在电子商务商店中模拟订单 SalesOrder有一个名为gift\u message\u id的属性 礼品消息id是一个整数值,是礼品消息模型的外键 GiftMessage模型有一种方法,可以采用订单模型并基于SalesOrder正确加载GiftMessage实例 我试图写一个测试来准确地测试这种行为,但我最终得到了两个模拟:一个是销售订单,一个是礼品信息,这没有意义。我做错了什么 我尝试测试的方法如

我正在尝试学习单元测试,但遇到了以下情况,我无法理解:

  • 我有一个模型:SalesOrder——它在电子商务商店中模拟订单
  • SalesOrder有一个名为gift\u message\u id的属性
  • 礼品消息id是一个整数值,是礼品消息模型的外键
  • GiftMessage模型有一种方法,可以采用订单模型并基于SalesOrder正确加载GiftMessage实例
  • 我试图写一个测试来准确地测试这种行为,但我最终得到了两个模拟:一个是销售订单,一个是礼品信息,这没有意义。我做错了什么

    我尝试测试的方法如下所示:

    public function loadGiftMessageByOrderModel(SalesOrder $order)
    {
        $giftMessageId = $order->getGiftMessageId();
    
        //if the order has a gift message id then load the gift message model and return it
        if ($giftMessageId !== false) {
            return new GiftMessage($giftMessageId);
        }
        return false;
    }
    
    public function loadGiftMessageByOrderModel(SalesOrder $order)
    {
        $giftMessageId = $order->getGiftMessageId();
    
        //if the order has a gift message id then load the gift message model and return it
        if ($giftMessageId !== false) {
            // $giftMessageLoader handles loading from the DB; it doesn't create a GiftMessage object
            $giftMessageRecord = $giftMessageLoader.loadById( $giftMessageId );
            // $giftMessageFactory actually calls the constructor to create a GiftMessage
            return $giftMessageFactory.createFromRecord( $giftMessageRecord );
        }
        return false;
    }
    

    考虑到订单和礼品信息数据存储在数据库中,如何对其进行单元测试。

    每当您有一个调用构造函数的方法,并且在构造函数中完成了非平凡的工作时,您都会遇到麻烦。我从上面看到的最佳选择是让上面的类保存一个
    GiftMessageFactory
    实例。然后,您可以模拟工厂以验证是否使用适当的值和适当的时间调用它。

    由于您已经编写了
    loadGiftMessageByOrderModel
    函数并解释了GiftMessage构造函数,我认为您无法轻松地对该代码进行单元测试。为了干净地测试它,您需要以而不是构造函数的方法从DB加载GiftMessage。构造函数不应该调用与数据库交互的方法。另一个类或方法应该执行该加载,然后调用构造函数。大概是这样的:

    public function loadGiftMessageByOrderModel(SalesOrder $order)
    {
        $giftMessageId = $order->getGiftMessageId();
    
        //if the order has a gift message id then load the gift message model and return it
        if ($giftMessageId !== false) {
            return new GiftMessage($giftMessageId);
        }
        return false;
    }
    
    public function loadGiftMessageByOrderModel(SalesOrder $order)
    {
        $giftMessageId = $order->getGiftMessageId();
    
        //if the order has a gift message id then load the gift message model and return it
        if ($giftMessageId !== false) {
            // $giftMessageLoader handles loading from the DB; it doesn't create a GiftMessage object
            $giftMessageRecord = $giftMessageLoader.loadById( $giftMessageId );
            // $giftMessageFactory actually calls the constructor to create a GiftMessage
            return $giftMessageFactory.createFromRecord( $giftMessageRecord );
        }
        return false;
    }
    
    然后,您可以模拟
    $giftMessageLoader.loadById
    调用,以便控制从数据库返回的内容。您还可以测试
    $giftMessageFactory
    是否正确调用
    GiftMessage
    构造函数并创建正确的对象


    这里的总的教训是构造函数应该很简单。他们可以验证参数,以便正确构造对象,但他们可能不应该调用应用程序的其他重要部分。

    我同意,单元测试是一个笑话,我认为它很有价值-但我只是不知道如何做到这一点,并使测试有价值,老实说,我真的不明白你的描述和你到底想测试什么。通常,如果要测试类X,可以模拟或实例化所有外部实体,而不管这些实体有多少。当然,保持松散耦合在这里是一个优势。我想测试一下,如果订单有礼品消息id,那么会返回礼品消息模型,否则会返回false。我可以很容易地模拟SalesOrder模型来返回礼物消息id。我不知道如何从该id加载新的礼物消息并返回它。这是因为GiftMessage构造函数获取一个id并从数据库加载它。您如何将db连接传递到
    GiftMessage
    对象?这里有一些全局状态,这使得您的系统很难被测试,并且可能真的有问题。