我有一个包含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和自动测试(而不是单独的测试数据库) - 相同的结果。
答案 0 :(得分:8)
我尝试构建API的最小版本,并在最新的Laravel 5.3.22版本上创建类似的测试。我还使用MariaDB 10.1.13作为数据库。事实证明,由于空api_token
,测试失败了。
这是我用于创建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');
}
}
这是我的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',
];
}
我在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);
});
最后,这是我的测试文件,用于测试我们的创建用户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。
另外作为参考,当我将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;
}