And you want to mock that service object on a controller test for example. Is it doable? The user is a very specific object that can change dynamically, so I can't see any way to inject that on the controller's constructor or method or getting it straight from the Service container.
Is it considered as a bad practice? to have arguments on service's constructor?
Laravel docs even though they are perfect, I find them weak on that specific subject. Can anyone clarify that?
@ninj4df I don’t really understand what you’re asking?
If you’re mocking the UserService then it doesn’t really matter what its constructor looks like; you’re mocking the UserService to turn it in to a “black box”. Therefore it doesn’t matter what its constructor takes because it will never be called.
$serviceMock = Mockery::mock(UserService::class);
// Now define method call expectations on $serviceMock here...
Am I missing something? Since UserService is initialized with a specific user on the controller, we don't get it from the service container, as Mocking expects. Maybe I am completely wrong, that's what I try to understand.
@Ninj4df But if you’re mocking the class, then you’re not instantiating it. You’re creating a mock of it, a mock on which you then define expectations for method calls.
// Create a mock of your UserService class...
$service = Mockery::mock(UserService::class);
$service->shouldReceive('someMethod')->once()->andReturn('some result');
// ...now call method on mock to get the mocked result
$service->someMethod(); // Returns 'some result', as per mock expectation
The $service variable holds a mock and not an instance of your UserService class. The UserService is never instantiated, therefore its constructor is never called, not does it matter what properties it has, because they’re never set nor accessed.
@martinbean but what's the point of this test? Let's say that you want to test a controller.
I want to test the following logic:
UserService class was called, method X was called one time. And if return was "true" for example , that the response was 200. If the return was "false" , the response was 404.
With your code, I have to execute the service on the test? I didn't get it 100% To be honest I got confused on that.
@Ninj4df Mocks are exactly that: mocks. They replace an object with a fake version. You use mocks in place of real objects.
I think you need to read up on what mocks are, as you seem a little confused. You wouldn’t mock an object, but then have that object call constructors or have state like class properties. That completely defeats the point of a mock.
You either call a real instance of an object, or a mock. If you’re testing a controller but don’t really care about the UserService (because it’s covered by another test) then you’d use a mock in place of the real service.
You either call a real instance of an object, or a mock. If you’re testing a controller but don’t really care about the UserService (because it’s covered by another test) then you’d use a mock in place of the real service.
Do you maybe have any example code for that?
I am looking for exactly this, everything works when we inject the service in the controller, but it does not work when we initialize the service inside the controller. That's my main problem that I don't find any solution for.