我正在关注 Laracast。(https://laracasts.com/series/lets-build-a-forum-with-laravel/episodes/28)我正试图让我的一项测试通过。我做了一些调试。在 RecordActivity.php 我目前有以下内容:
static::deleting(function ($thread) {
$thread->replies->each(function($reply) {
dd($reply);
$reply->delete();
});
});
但是,当我运行在课程中创建的测试时,在我看来,闭包each()
永远不会触发,因为测试返回错误而不是吐出回复。我重看了几次视频,并将我的代码与 github repo 中的 Jeffery 代码进行了比较。
我究竟做错了什么?
记录活动.php
<?php
namespace App;
trait RecordActivity
{
protected static function bootRecordActivity()
{
if (auth()->guest()) return;
foreach (static::getRecordEvents() as $event) {
static::$event(function ($model) use ($event) {
$model->recordActivity($event);
});
}
static::deleting(function ($model) {
$model->activity()->delete();
});
}
protected function recordActivity($event)
{
$this->activity()->create([
'user_id' => auth()->id(),
'type' => $this->getActivityType($event)
]);
}
protected static function getRecordEvents()
{
return ['created'];
}
public function activity() {
return $this->morphMany('App\Activity', 'subject');
}
protected function getActivityType($event)
{
$type = strtolower((new \ReflectionClass($this))->getShortName());
return "{$event}_{$type}";
}
}
线程.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Thread extends Model
{
use RecordActivity;
protected $guarded = [];
protected $with = ['creator', 'channel'];
protected static function boot()
{
parent::boot();
static::addGlobalScope('replyCount', function ($builder) {
$builder->withCount('replies');
});
static::deleting(function ($thread) {
$thread->replies->each(function($reply) {
dd($reply);
$reply->delete();
});
});
}
public function path() {
return page_url('forum',"threads/" . $this->channel->slug . '/'. $this->id);
}
public function replies() {
return $this->hasMany(Reply::class);
}
public function channel() {
return $this->belongsTo(Channel::class);
}
public function creator()
{
return $this->belongsTo(User::class, 'user_id');
}
public function addReply($reply) {
$this->replies()->create($reply);
}
public function scopeFilter($query, $filters) {
return $filters->apply($query);
}
}
CreateThreadsTest.php
<?php
namespace Tests\Feature;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;
use App\Thread;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class CreateThreadsTest extends TestCase
{
use DatabaseMigrations;
function test_guests_may_not_create_threads() {
$thread = make('App\Thread');
$this->withExceptionHandling()->post(page_url('forum','threads'), $thread->toArray())->assertRedirect('/login');
}
function test_an_authenticated_user_can_create_new_forum_threads() {
$this->signIn();
$thread = make('App\Thread');
$response = $this->post(page_url('forum','/threads'), $thread->toArray());
$this->get($response->headers->get('Location'))
->assertSee($thread->title)
->assertSee($thread->body);
}
function test_a_thread_requires_a_title()
{
$this->publishThread(['title' => null])->assertSessionHasErrors(['title']);
}
function test_a_thread_requires_a_body()
{
$this->publishThread(['body' => null])->assertSessionHasErrors(['body']);
}
function test_a_thread_requires_a_channel_id()
{
factory('App\Channel', 2)->create();
$this->publishThread(['channel_id' => 999])->assertSessionHasErrors(['channel_id']);
}
function test_guests_cannot_delete_threads() {
$thread = create('App\Thread');
$this->delete($thread->path())->assertRedirect('/login');
$this->signIn();
$this->delete($thread->path())->assertStatus(403);
}
//
// function test_threads_may_only_be_deleted_by_those_who_have_permission() {
//
// }
function test_authorized_users_can_delete_threads() {
$this->signIn();
$thread = create('App\Thread', ['user_id' => auth()->id()]);
$reply = create('App\Reply', ['thread_id' => $thread->id]);
$response = $this->json('DELETE', $thread->path());
$response->assertStatus(204);
$this->assertDatabaseMissing('threads', ['id' => $thread->id]);
$this->assertDatabaseMissing('replies', ['id' => $reply->id]);
$this->assertDatabaseMissing('activities', ['subject_id' => $thread->id, 'subject_type' => get_class($thread)]);
$this->assertDatabaseMissing('activities', ['subject_id' => $reply->id, 'subject_type' => get_class($reply)]);
}
public function publishThread($overrides) {
$this->withExceptionHandling()->signIn();
$thread = make('App\Thread', $overrides);
return $this->post(page_url('forum','/threads'), $thread->toArray());
}
}
线程控制器.php
<?php
namespace App\Http\Controllers;
use App\Filters\ThreadFilters;
use Illuminate\Http\Request;
use App\Thread;
use App\Channel;
use Auth;
class ThreadsController extends Controller
{
public function __construct() {
$this->middleware('auth')->except(['index', 'show']);
}
/**
* Display a listing of the resource.
*
* @param Channel $channel
* @param \App\Http\Controllers\ThreadFilters $filter
* @return \Illuminate\Http\Response
*/
public function index(Channel $channel, ThreadFilters $filter)
{
$threads = $this->getThread($channel, $filter);
if(request()->wantsJson()) {
return $threads;
}
return view('threads.index', compact('threads'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('threads.create');
}
/**
// * Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate(request(), [
'title' =>'required',
'body' => 'required',
'channel_id' => 'required|exists:channels,id'
]);
$thread = Thread::create([
'user_id' => Auth::user()->id,
'channel_id' => request('channel_id'),
'title' => request('title'),
'body' => request('body')
]);
return redirect($thread->path());
}
/**
* Display the specified resource.
*
* @param int $id
*/
public function show($channelSlug, Thread $thread)
{
return view('threads.show', [
'thread' => $thread,
'replies' => $thread->replies()->paginate(25)
]);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $channel
* @return \Illuminate\Http\Response
*/
public function destroy($channel, Thread $thread)
{
$this->authorize("update", $thread);
$thread->replies()->delete();
$thread->delete();
if(request()->wantsJson()) {
return response([], 204);
}
return redirect(page_url('forum','/threads'));
}
protected function getThread(Channel $channel, ThreadFilters $filter) {
$threads = Thread::latest()->filter($filter);
if($channel->exists) {
$threads->where('channel_id', $channel->id);
}
return $threads->get();
}
}
<?php
namespace App\Http\Controllers;
use App\Filters\ThreadFilters;
use Illuminate\Http\Request;
use App\Thread;
use App\Channel;
use Auth;
class ThreadsController extends Controller
{
public function __construct() {
$this->middleware('auth')->except(['index', 'show']);
}
/**
* Display a listing of the resource.
*
* @param Channel $channel
* @param \App\Http\Controllers\ThreadFilters $filter
* @return \Illuminate\Http\Response
*/
public function index(Channel $channel, ThreadFilters $filter)
{
$threads = $this->getThread($channel, $filter);
if(request()->wantsJson()) {
return $threads;
}
return view('threads.index', compact('threads'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('threads.create');
}
/**
// * Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate(request(), [
'title' =>'required',
'body' => 'required',
'channel_id' => 'required|exists:channels,id'
]);
$thread = Thread::create([
'user_id' => Auth::user()->id,
'channel_id' => request('channel_id'),
'title' => request('title'),
'body' => request('body')
]);
return redirect($thread->path());
}
/**
* Display the specified resource.
*
* @param int $id
*/
public function show($channelSlug, Thread $thread)
{
return view('threads.show', [
'thread' => $thread,
'replies' => $thread->replies()->paginate(25)
]);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $channel
* @return \Illuminate\Http\Response
*/
public function destroy($channel, Thread $thread)
{
$this->authorize("update", $thread);
$thread->replies()->delete();
$thread->delete();
if(request()->wantsJson()) {
return response([], 204);
}
return redirect(page_url('forum','/threads'));
}
protected function getThread(Channel $channel, ThreadFilters $filter) {
$threads = Thread::latest()->filter($filter);
if($channel->exists) {
$threads->where('channel_id', $channel->id);
}
return $threads->get();
}
}
PHPUnit 错误:
PHPUnit 7.5.18 by Sebastian Bergmann and contributors.
.
................F............ 30 / 30 (100%)
Time: 15.02 seconds, Memory: 30.00 MB
There was 1 failure:
1) Tests\Feature\CreateThreadsTest::test_authorized_users_can_delete_threads
Failed asserting that a row in the table [activities] does not match the attributes {
"subject_id": 1,
"subject_type": "App\\Reply"
}.
Found: [
{
"id": "2",
"user_id": "1",
"subject_type": "App\\Reply",
"subject_id": "1",
"type": "created_reply",
"created_at": "2019-12-23 20:26:03",
"updated_at": "2019-12-23 20:26:03"
}
].
/home/vagrant/Code/intransportal/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:44
/home/vagrant/Code/intransportal/tests/Feature/CreateThreadsTest.php:75
为什么我的测试没有抛弃$reply
模型