@bwrigley
I'd be interested to know more please.
It makes sense to me to have that kind of test if you can somehow create one test and let it test all routes on your app, but that gets messy very fast. If not then I would discard it. I've had similar approach when I started testing but then when something changes you have to fix same issue in multiple places since you will certainly add a test to see what happens when you have data in that invoices method.
Lately I've stopped writing tests that prove negative statements cause it is:
- easier to read and understand positive one especially when you work in a multinational team with no native english speaking members,
- and you can come up with a bunch of cases which prove negative scenarios but problem is that they have no value to non technical people in company, they take time to maintain and you can never come up with all cases
Example for this istest_guest_can_not_access_some_url. That is guarded by middleware which I do not own and there is no point for me to test it since it is a built in feature which if it had any issues someone would have caught it by now and fix it if we take into account how many people use same framework.
I know every company says in their ads that they want TDD, but the truth is that they do not care about that and they want you to do as much as possible as fast as possible. That mostly comes down to cutting test time (writing/maintaining) since that is nothing business side of company cares about. They just want less bugs and more features.
There really isn't any logic in that method that warrants testing, as it's not mine. But I would like to a test to make sure that the route is available and is returning a view and a 200.
The way you've picked then breaks the rule Do not mock what you do not own. You do not own that billable trait which provides you with invoices method, and you do not own User model cause it ships with the framework, and you just mocked it.
That is why you should go with a service class or repository so you can mock since you will be introducing that class. Even if it is a class with one method that calls that same logic you have in your controller. I guess I should not point out what improvements does it bring to the code if you introduce a new class to handle that case.
Is it that this feel more like a unit test? I guess as it's a controller method, I thought this was really the only to way to test it.
There is no clear line what is unit and what is feature test. I guess that unit way to test this if you wrote something like this:
public function test_can_not_come_up_with_a_name_at_this_moment(): void
{
$controller = new SomeController();
$user = new User();
$response = $controller->index($user);
$this->assertSomething($response);
}
The code above would be a living hell if that controller or method gets complicated as they tend to in real life. That is why it feels to me more as feature test since you are accessing it as user of that application would, from outside. And when you access it from outside you trigger a bunch of features which then more feels like a feature test. That way you cover more ground.
What I think is that it makes sense to have unit test if you are building a calculator class, cause everything is probably contained within one class and it is transparent cause result depends directly on what you pass to it. If you do not have that kind of code then you are better of with feature test, since they are easier to write. You have your route, you know which data you need to send via request and you know what changes that request caused in the database. If you have any events, just assert that event was fired and write separate feature test for listeners that wait for that event.
Hope all this makes any sense, I'm still struggling to write any articulate text in english.