Laravel测试未验证数据库约束

时间:2016-11-10 16:11:31

标签: laravel laravel-5 phpunit

我有一个包含users表的Laravel 5.3应用程序。该表上的一个字段是api_token,它在数据库定义中设置为NOT NULL。如果我使用Postman并点击我的API端点来创建用户而不包含api_token字段,则会收到以下错误:SQLSTATE[HY000]: General error: 1364 Field 'api_token' doesn't have a default value

我还没有为生成令牌添加代码,所以这正是我所期望的。但是,我有这个PHPUnit测试:

/** @test */
public function a_user_can_be_created()
{
    $this->user_data = [
        'first_name' => 'Jane',
        'last_name' => 'Smith',
        'email_address' => 'jsmith@mailinator.com',
        'phone' => '1234567890',
        'username' => 'jsmith',
        'password' => 'testing'
    ];

    $this->post('/api/users', $this->user_data, array(
                'Accept' => 'application/json'
            ))
         ->assertResponseStatus(201)
         ->seeJsonEquals(['status' => 'success'])
         ->seeInDatabase('users', $this->user_data);
}

问题在于测试通过了。当用户保存数据库时应该抛出异常,该测试如何成功?

我已经在各个点死亡并被丢弃,并确认在自动化测试运行时数据实际上已保存到数据库中。我也尝试使用相同的数据库进行Postman和自动测试(而不是单独的测试数据库) - 相同的结果。

4 个答案:

答案 0 :(得分:8)

我尝试构建API的最小版本,并在最新的Laravel 5.3.22版本上创建类似的测试。我还使用MariaDB 10.1.13作为数据库。事实证明,由于空api_token,测试失败了。

1。用户迁移文件

这是我用于创建users表格的迁移文件:

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('first_name');
            $table->string('last_name');
            $table->string('email_address')->unique();
            $table->string('phone');
            $table->string('username');
            $table->string('password');
            $table->string('api_token');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('users');
    }
}

2。用户模型

这是我的app\User.php文件的样子。非常简单,我只更新$fillable属性以匹配我们的users表结构:

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'first_name',
        'last_name',
        'email_address',
        'phone',
        'username',
        'password',
        'api_token',
    ];

    protected $hidden = [
        'password',
    ];
}

3。 API路由文件

我在routes/api.php文件上创建了一个新的API路由来创建一个新用户。超级简单,无需任何验证:

Route::post('users', function (Request $request) {
    $data = $request->only([
        'first_name',
        'last_name',
        'email_address',
        'phone',
        'username',
        'password',
        'api_token',
    ]);

    if (is_string($data['password'])) {
        $data['password'] = bcrypt($data['password']);
    }

    $user = App\User::create($data);

    return response()->json([
        'status' => 'success',
    ], 201);
});

4。创建用户API测试

最后,这是我的测试文件,用于测试我们的创建用户API。

class ApiTest extends TestCase
{
    /** @test */
    public function a_user_can_be_created()
    {
        $user = [
            'first_name' => 'Jane',
            'last_name' => 'Smith',
            'email_address' => 'jsmith@mailinator.com',
            'phone' => '1234567890',
            'username' => 'jsmith',
        ];

        $this->json('POST', 'api/users', $user)
            ->assertResponseStatus(201)
            ->seeJsonEquals(['status' => 'success'])
            ->seeInDatabase('users', array_except($user, ['password']));
    }
}

唯一的区别是我使用array_except()从数据库测试中排除password列,因为我们在将值存储到数据库之前对其进行了散列。但当然这与你的问题无关。

当我在没有设置api_token的情况下运行测试时,测试按预期失败。 API返回状态代码500,而我们预计它是201。

The create user API failed!

另外作为参考,当我将api_token列设置为不可为空时,然后强制创建一个空api_token的新用户,我收到的异常消息是这样的:

  

Connection.php第769行中的QueryException:

     

SQLSTATE [23000]:完整性约束违规:1048列   ' api_token'不能为null(SQL:插入用户...

希望这可以帮助您调试问题!

答案 1 :(得分:2)

我假设您使用的是mysql。在mysql中,NOT NULL存在问题。它是这样的。虽然字段为NOT NULL,但您可以在mysql中将其留空并且查询通过。 mysql中的NOT NULL表示您不能在该字段中拥有NULL值,而不能将其留空。我假设你创建了这个用户

User::create($request->all()) 

如果您尝试打印生成的SQL查询

dd((new User($request->all())->toSql())

您会看到生成的查询包含''字段

api_token

我切换到posgres主要是因为这个问题。

如果你想解决这个问题,你有几个选择。一种是让api_token字段可以为空。第二个是手动填充模型

答案 2 :(得分:1)

您应该在api_token表格中为users列定义默认值,或者将其设置为nullable,您的用户迁移表格将如下所示:

Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('api_token')->nullable();
    // Or you can set it to default value - 0
    // $table->string('api_token')->default('0');
    $table->timestamps();
});

希望这有帮助!

答案 3 :(得分:1)

我认为问题在于单元测试。为什么不尝试使用Eloquent进行Laravel测试?像这样:

public function a_user_can_be_created()
{
    $user = new User;

    $user->first_name = 'Jane';
    $user->last_name = 'Smith';
    $user->email_address = 'jsmith@mailinator.com';
    $user->phone = '1234567890';
    $user->username = 'jsmith';
    $user->password = 'testing';

    $user->save();

    return $user;
}

https://laravel.com/docs/5.3/testing