For the conclusion of this course, I want to leave you with three more tips or pieces of advice that you will walk away with thinking of how to structure your tests in the future.
The first advice is to think about scenarios. What methods would you use in your tests?
This is way more important than the syntax of writing a test.
An example of that could be Laravel Cashier subscriptions test, which has test subscriptions can be created and a lot of things are happening inside this test. Then test swapping subscription with coupon, swapping subscription and preserving quantity, and many other tests.
This file is not for testing just the subscriptions that happen and are canceled; it is for thinking about actual, real-life scenarios.
What may happen in the journey of your, for example, Eloquent model or entity? Try to think like a user and test scenarios like swapping subscriptions with various parameters.
You're writing the scenario, and you want to test as many real-life scenarios as possible, including the edge cases, because those are exactly the ones that may bite back in the future.
This was tip number one. Come up with as many scenarios as possible that may be realistic in your project and cover that with future tests.
The second advice is to avoid creating too long classes, such as Controllers, because they would be harder to test.
For example, here is a Controller with a lot of logic. There is authorization, validation, creating data for more than one table, and sending notifications.
class PostController extends Controller{ public function store(Request $request) { $this->authorize('user_create'); $userData = $request->validate([ 'name' => 'required', 'email' => 'required|unique:users', 'password' => 'required', ]); $userData['start_at'] = Carbon::createFromFormat('m/d/Y', $request->start_at)->format('Y-m-d'); $userData['password'] = bcrypt($request->password); $user = User::create($userData); $user->roles()->sync($request->input('roles', [])); Project::create([ 'user_id' => $user->id, 'name' => 'Demo project 1', ]); Category::create([ 'user_id' => $user->id, 'name' => 'Demo category 1', ]); Category::create([ 'user_id' => $user->id, 'name' => 'Demo category 2', ]); MonthlyReport::where('month', now()->format('Y-m'))->increment('users_count'); $user->sendEmailVerificationNotification(); $admins = User::where('is_admin', 1)->get(); Notification::send($admins, new AdminNewUserNotification($user)); return response()->json([ 'result' => 'success', 'data' => $user, ], 200); }}
This is just an example of a too-long class. You can use the store method in the feature test and assert that records were created or failed for some reason. However, it is almost impossible to have separate unit tests about validation, for example, the word count for the post and whether it's correct or not.
General advice is to separate your code and refactor it into smaller units. Then, those would be easier to cover in unit tests in addition to the feature tests.
I would call this an advanced level of testing. Feature tests are the basic level of automated testing, testing all the endpoints and URLs, and unit tests are the top level, ensuring that separate units are okay. But for that to happen, you need to have those as individual units, like service classes, action classes, repositories, or whatever design pattern you prefer. Just have shorter methods, and this will be easier to test.
The third piece of advice related to unit and feature tests is based on a tweet by Chris Arter and an image of Titanic, which represents the Feature and Unit tests very well.
I really like this picture, and I'm a big fan of having Feature tests first, but again, not just for successful scenarios but for all the possible scenarios. Then, on top of that, you may additionally test unit by unit.
The core goal of testing is to ensure that your features will work. However, if you want/need to go deeper, you can cover the units with unit tests.
I hope this course gave you enough examples, and now you can implement everything. If you have any more questions, or if I skipped something or should have gone more in-depth, I may write additional lessons or posts or shoot additional videos in the future on my YouTube channel or elsewhere or just tweet something. Shoot in the comments below, and see you guys in other courses.