【问题标题】:How can I write a test in this case?在这种情况下如何编写测试?
【发布时间】:2021-11-30 18:49:41
【问题描述】:

附: 起初我问如何模拟 Model::create,但我想做的是编写一个测试,当 try 发生错误时,它会正确地进入 catch。

感谢@matiaslauriti 的评论,我找到了更好的方法,所以我将把我的问题从如何模拟改为如何编写测试。

详情请看答案部分和cmets。

·········

我有这个代码。

class MyModel extends Model
{
    public static function addRecord()
    {
        //...

        try {
            DB::beginTransaction();

            foreach ($rows as $row) {
                self::create([
                    'name'         => $name,
                    'phone'         => '',
                ]);
            }

            DB::commit();
        } catch (\Exception $ex) {
            DB::rollback();

            throw($e);
        }

    }
}

这是我的测试代码。但是模拟返回错误并且测试不起作用。

class MyModelTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        $this->my_model = new MyModel();

    }

    /**
     * @test
     */
    public function addRecord_error()
    {
        $mock = \Mockery::mock(MyModel::class)->makePartial();
        $mock->shouldReceive('create')
      ->once()
          ->andThrow(new \Exception());

        $this->app->bind(MyModel::class, function () use ($mock) {
            return $mock;
        });

     $my_model = app(MyModel::class);

        //run method
        $my_model->addRecord();  

        // ↑↑ This return error with 
              //BadMethodCallException:Static method 
              //Mockery_2_App_Models_MyModel::addRecord() does not exist on this mock object

        //try {
           //$my_model->addRecord();

        //} catch (\Exception $ex) {

          // $this->assertCount(0, MyModel::all());
        //}
    }
}

我想写一个测试,如果尝试失败,它会去捕获并调用回滚。

【问题讨论】:

  • 首先,你正在使用new MyModel();,你永远不会得到mock,因为你实际上是在使用new,所以你正在创建一个新实例......您需要做的是使用app(MyModel::class);resolve(MyModel::class);resolveapp 的别名)。在声明期望后(在shouldReceive->....->.... 之后)并使用$this->app->bind(MyModel::class, $mock),而不是$this->instance,您必须使用这些助手中的任何一个。您还可以为第一部分设置“更好”的代码:laravel.com/docs/7.x/database#database-transactions
  • @matiaslauriti 谢谢你的评论!我尝试按上述方式更改代码,但出现错误,而且似乎还没有通过模拟。还有什么我应该做的吗?也感谢您提供更好的代码。由于我只负责测试,所以我无法更改产品方法,但我会记住这一点。
  • 让我添加一个答案

标签: unit-testing phpunit laravel-7 mockery


【解决方案1】:

您的测试代码应如下所示:

use Exception;
use Mockery\Mockery;

class MyModelTest extends TestCase
{
    /**
     * @test
     */
    public function addRecord_error()
    {
        $mock = Mockery::mock(MyModel::class)->makePartial();
        $mock->shouldReceive('create')
      ->once()
          ->andThrow(new Exception);

        $my_model = app(MyModel::class);

        try {
            $my_model::addRecord();
        } catch (Exception $exception) {
           $this->assertCount(0, Model::all());
        }
    }
}

如果您在其内部使用模型,则必须在 Service Container 辅助函数之前将模拟添加到 app

$this->app->bind(MyModel::class, function () use ($mock) {
    return $mock;
});

请记住,$this->assertCount(0, Model::all()); 应替换为您要测试的模型(如果它已回滚)。因此,将Model 替换为所需的Model

但是,这应该一直通过,因为您在第一次运行时抛出错误,所以您永远不会添加任何东西。所以assertCount应该一直通过,去掉还是不去掉都是一样的。

【讨论】:

  • 再次感谢您。我重写了上面的方法,但它返回错误“在这个模拟对象上不存在”。 1:create Mock 2:$this->app->bind... 3:app(MyModel::class) 4: run test method 顺序是否正确?抱歉问了这么多问题..
  • 问多少问题就问多少,不用担心。你能在这里分享完整的错误吗?
  • 非常感谢您的帮助。错误是“Mockery\Exception\BadMethodCallException:此模拟对象上不存在静态方法 Mockery_2_App_Models_MyModel::addRecord()”。 $exception 中的消息返回了相同的错误,因此我尝试在 try catch 之外调用该函数并收到此错误。 (我已将原问题修改为当前代码)
  • 我不知道发生了什么,因为您没有分享完整的代码。但是,这不应该像这样测试。我知道这是您从贵公司获得的一张票,但要测试您想要测试的内容更难。为什么create 会抛出错误?数据库是外部数据库吗?
  • 我刚开始写测试不久,所以我觉得每一个测试都很困难..但我会根据你教给我的内容来创建测试。非常感谢!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-21
  • 2013-11-03
  • 2012-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多