Cannot mock class: Mockery\Exception\InvalidCountException - laravel

I try to run a phpunit test for my Laravel 6.0 application, but when trying to mock a method, I get this message as it cannot find the class/method:
Mockery\Exception\InvalidCountException: Method scrapeGoogleData() from Mockery_2_App_Http_Controllers_Scraper should be
called exactly 1 times but called 0 times.
My test code is:
namespace Tests\Feature;
use Tests\TestCase;
use App\Http\Controllers\Scraper;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ScrapeTest extends TestCase
{
use RefreshDatabase;
/** #test */
public function test_scrapeGoogleData() {
$this->mock(Scraper::class, function ($mock) {
$mock->shouldReceive('scrapeGoogleData')->once();
});
Scraper::scrape('www.google.com');
}
The "scrapeGoogleData" method is definitely ran as it's called from the Scraper::scrape method. But for some reason, mockery cannot see that. I get this error:
Mockery\Exception\InvalidCountException: Method scrapeGoogleData() from Mockery_2_App_Http_Controllers_Scraper should be
called exactly 1 times but called 0 times.
What am I doing wrong?

You're mocking the whole class, so the code in your scrape method is not run. You'll have to make a partial mock, to keep any method which is not explicitly mocked.
EDIT:
You'll have to resolve it from the container and then call scrape on that:
app(Scrape::class)->scrape('www.google.com');

Related

Laravel 5.6 testing Notification::assertSentTo() not found

Struggling since multiple days to get Notification::assertSentTo() method working in my feature test of reset password emails in a Laravel 5.6 app, yet receiving ongoing failures with following code:
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\Facades\Notification;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserPasswordResetTest extends TestCase
{
public function test_submit_password_reset_request()
{
$user = factory("App\User")->create();
$this->followingRedirects()
->from(route('password.request'))
->post(route('password.email'), [ "email" => $user->email ]);
Notification::assertSentTo($user, ResetPassword::class);
}
}
I have tried several ideas including to use Illuminate\Support\Testing\Fakes\NotificationFake directly in the use list.
In any attempt the tests keep failing with
Error: Call to undefined method Illuminate\Notifications\Channels\MailChannel::assertSentTo()
Looking forward to any hints helping towards a succesful test.
Regards & take care!
It seems like you are missing a Notification::fake(); For the correct fake notification driver to be used.
Notification::fake();
$this->followingRedirects()
...

How to mock only one method with Laravel using PhpUnit

I have this test :
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Services\AccessTokenService;
use App\Services\MemberService;
class BranchTest extends TestCase
public function testPostBranchWithoutErrors()
{
$this->mock(AccessTokenService::class, function ($mock) {
$mock->shouldReceive('introspectToken')->andReturn('introspection OK');
});
$this->mock(MemberService::class, function ($mock) {
$mock->shouldReceive('getMemberRolesFromLdap')->andReturn(self::MOCKED_ROLES);
});
As you see, there are 2 mocks on this test. The 2nd one 'MemberService:class' is my current problem. In this class there are 2 functions : 'createMember' and 'getMemberRolesFromLdap'. I precise that I want to mock only the 'getMemberRolesFromLdap' function.
In the documentation, it is written :
You may use the partialMock method when you only need to mock a few methods of an object. The methods that are not mocked will be executed normally when called:
$this->partialMock(Service::class, function ($mock) {
$mock->shouldReceive('process')->once();
});
But when I use "partialMock", I have this error:
Error: Call to undefined method Tests\Feature\BranchTest::partialMock()
And when I try a classic mock (no partial), I have this error:
Received Mockery_1_App_Services_MemberService::createMember(), but no expectations were specified
certainly because there are 2 functions in this class and so PhpUnit does not know what to do with the function 'createMember'.
What can I try next? I am a beginner to PhpUnit tests.
Edit
Laravel 6.0
PhpUnit 7.5
PartialMock shorthand is firstly added in Laravel 6.2 see the release notes. So an upgrade to 6.2 should fix your problem.
Secondly you can add the following snippet to your Tests\TestCase.php class and it should work.
protected function partialMock($abstract, Closure $mock = null)
{
return $this->instance($abstract, Mockery::mock(...array_filter(func_get_args()))->makePartial());
}

Mock out function call to another function in same class

I have function that calls out to another function in the same class. I'm trying to test if that method is called in my unit tests however can't find a way to mock it.
Here is an example of the method call
public function follow($user_id, $followsfeed, $target_user_id)
{
$news_feeds = $this->getNewsFeeds($user_id);
}
I want to mock the method getNewsFeed and assert it is being called when I execute follow.
What is the best way to achieve this. as I can't workout how to create an internal mocked object.
You can use a partial mocks, as example on your test class, you can do:
public function test_follow()
{
$mock = Mockery::mock('App\Services\SomeService')->makePartial();
$mock->shouldReceive('getNewsFeeds')
->once()
->with("id1","id2")
->andReturn($someNews);
$mock-> follow("id1","id2");
}
You can also use the PHPUnit test-doubles
Hope this help

How to autoload custom class in Laravel?

I wrote custom class with method that returns array.
I need to autoload this class like Auth() class in Laravel, that I could get access to it from any controller not using use
Create one custom helper file
and add function
if (! function_exists('yourcustomclass')) {
function yourcustomclass()
{
use App\Http\yourcustomclassname;
return new yourcustomclassname()
}
}
you can use yourcustomclass() function from anywhere to get yourcustomclassname class object
When accessing class/function from other namespace than you're currently in you have to use Fully-Qualified Class Name (or type use, but you don't want to do that), so instead of Auth::user() you need to write \Auth::user()
\ at the beginning means that class is located in root namespace
Why don't you write super method(s) in App\Http\Controllers\Controller?
Simply call super method(s) in the subclass which extends Controller

Laravel - Running method of another controller in one controller

I have UserController and PetController.
In my UserController, I have rewardUser() method.
in my PetController, I'm using the $user variable which indicates the current logged in user.
How I can run my rewardUser() method from my PetController?
I've been trying to user $user->rewardUser(); but for some reasons its not recognizing my method this way.
"Call to undefined method Illuminate\Database\Query\Builder::rewardUser()"
The best way is to use a trait.
Create a trait file, in App\Common.php, for e.g. Then copy the rewardUser() method to the trait.
Your trait file:
namespace App\Forum;
trait Common {
public function rewardUser() {
// Your code here...
}
}
Then in yourUserController.php and PetController.php, use the trait.
// UserController and PetController.php
namespace App\Http\Controllers
use App\Common; // <- Your trait
class UserController extends Controller {
use Common // <- Your trait
public function doSomething() {
// Call the method from both your controllers now.
$this-rewardUser();
}
}
You can use the straight in as many controllers as you want and you can call the method in the straight using $this->methodName().
Very simply and effective.
It seems like you are missing some structure concepts, but if you really need it, you may use the container to do so:
$userController = app()->make(UserController::class);
return app()->call([$userController, 'rewardUser']);
may be you should define the method rewardUser() in the User Model and import it with use App\User

Resources