Method App\Http\Controllers\FriendshipController::assertCount does not exist - laravel

user can not send a friend request if friendship is pending
public function user_can_not_send_a_friend_request_if_frienship_is_pending($friend_id)
{
$sender = User::find(Auth::id());
$recipient = User::find($friend_id);
$sender->befriend($recipient);
$sender->befriend($recipient);
$sender->befriend($recipient);
// return $sender;
$this->assertCount(1,$recipient->getFriendRequests());
}
but it returns me method assertcount does not exist

assertCount() is actually a function that comes after extending TestCase class So it will be available only in Testcase classes that are extended from TestCase.
In Controllers you have to do as following:
if ($recipient->getFriendRequests()->count() === 1) {
//do something here
}

Related

Mocking class instantiation in a feature test

I'm currently working on my first Laravel project — a service endpoint that returns a resource based on a recording saved in S3. The service doesn't require a DB, but my idea was, I could keep the controller skinny, by moving the logic to a "model". I could then access the resource by mimic'ing some standard active record calls.
Functionally, the implementation works as expected, but I am having issues with mocking.
I am using a library to create signed CloudFront URLs, but it is accessed as a static method. When I first started writing my feature test, I found that I was unable to stub the static method. I tried class aliasing with Mockery, but with no luck — I was still hitting the static method. So, I tried wrapping the static method in a little class assuming mocking the class would be easier. Unfortunately, I'm experiencing the same issue. The thing that I am trying to mock is being hit as if I'm not mocking it.
This stack overflow post gives an example of how to use class aliasing, but I can't get it to work.
What is the difference between overload and alias in Mockery?
What am I doing wrong? I'd prefer to get mockery aliasing to work, but instance mocking would be fine. Please point me in the right direction.
 Thank you in advance for your help.
Controller
// app/Http/Controllers/API/V1/RecordingController.php
class RecordingController extends Controller {
public function show($id){
return json_encode(Recording::findOrFail($id));
}
}
Model
// app/Models/Recording.php
namespace App\Models;
use Mockery;
use Carbon\Carbon;
use CloudFrontUrlSigner;
use Storage;
use Illuminate\Support\Arr;
class Recording
{
public $id;
public $url;
private function __construct($array)
{
$this->id = $array['id'];
$this->url = $this->signedURL($array['filename']);
}
// imitates the behavior of the findOrFail function
public static function findOrFail($id): Recording
{
$filename = self::filenameFromId($id);
if (!Storage::disk('s3')->exists($filename)) {
abort(404, "Recording not found with id $id");
}
$array = [
'id' => $id,
'filename' => $filename,
];
return new self($array);
}
// imitate the behavior of the find function
public static function find($id): ?Recording
{
$filename = self::filenameFromId($id);
if (!Storage::disk('s3')->exists($filename)){
return null;
}
$array = [
'id' => $id,
'filename' => $filename,
];
return new self($array);
}
protected function signedURL($key) : string
{
$url = Storage::url($key);
$signedUrl = new cloudFrontSignedURL($url);
return $signedUrl->getUrl($url);
}
}
/**
* wrapper for static method for testing purposes
*/
class cloudFrontSignedURL {
protected $url;
public function __construct($url) {
$this->url = CloudFrontUrlSigner::sign($url);
}
public function getUrl($url) {
return $this->url;
}
}
Test
// tests/Feature/RecordingsTest.php
namespace Tests\Feature;
use Mockery;
use Faker;
use Tests\TestCase;
use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Foundation\Testing\WithFaker;
/* The following is what my test looked like when I wrapped CloudFrontUrlSigner
* in a class and attempted to mock the class
*/
class RecordingsTest extends TestCase
{
/** #test */
public function if_a_recording_exists_with_provided_id_it_will_return_a_URL()
{
$recordingMock = \Mockery::mock(Recording::class);
$faker = Faker\Factory::create();
$id = $faker->numberBetween($min = 1000, $max = 9999);
$filename = "$id.mp3";
$path = '/api/v1/recordings/';
$returnValue = 'abc.1234.com';
$urlMock
->shouldReceive('getURL')
->once()
->andReturn($returnValue);
$this->app->instance(Recording::class, $urlMock);
Storage::fake('s3');
Storage::disk('s3')->put($filename, 'this is an mp3');
Storage::disk('s3')->exists($filename);
$response = $this->call('GET', "$path$id");
$response->assertStatus(200);
}
}
// The following is what my test looked like when I was trying to alias CloudFrontUrlSigner
{
/** #test */
public function if_a_recording_exists_with_provided_id_it_will_return_a_URL1()
{
$urlMock = \Mockery::mock('alias:Dreamonkey\cloudFrontSignedURL');
$faker = Faker\Factory::create();
$id = $faker->numberBetween($min = 1000, $max = 9999);
$filename = "$id.mp3";
$path = '/api/v1/recordings/';
$returnValue = 'abc.1234.com';
$urlMock
->shouldReceive('sign')
->once()
->andReturn($returnValue);
$this->app->instance('Dreamonkey\cloudFrontSignedURL', $urlMock);
Storage::fake('s3');
Storage::disk('s3')->put($filename, 'this is an mp3');
Storage::disk('s3')->exists($filename);
$response = $this->call('GET', "$path$id");
$response->assertStatus(200);
}
}
phpunit
$ phpunit tests/Feature/RecordingsTest.php --verbose
...
There was 1 failure:
1) Tests\Feature\RecordingsTest::if_a_recording_exists_with_provided_id_it_will_return_a_URL
Expected status code 200 but received 500.
Failed asserting that false is true.
/Users/stevereilly/Projects/media-service/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:133
/Users/stevereilly/Projects/media-service/tests/Feature/RecordingsTest.php:85
/Users/stevereilly/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:206
/Users/stevereilly/.composer/vendor/phpunit/phpunit/src/TextUI/Command.php:162
You're getting a 500, which means there's something wrong with the code. Just by scanning it I notice you're missing a filenameFromId method on the Recordings class, and the Test is creating a mock named $recordingMock, but you try to use $urlMock. Try to fix those issues first.
Then you're mocking the class, but you never replace it in your application (you did it in the old test apparently).
Generally you want to follow these steps when mocking:
1. Mock a class
2. Tell Laravel to replace the class with your mock whenever someone requests it
3. Make some assertions against the mock

Laravel + Mockery InvalidCountException

I am trying to mock a class to prevent it from having to call 3rd party apis. But when setting up the mock, it doesn't seem to affect the controller action. I did try replacing the $this->postJson() by manually creating instances of the Request- and OEmbedController-classes. The create()-method is getting called, but I am receiving an error from Mockery that it isn't.
What am I doing wrong here?
Error:
Mockery\Exception\InvalidCountException : Method create() from Mockery_2_Embed_Embed should be called exactly 1 times but called 0 times.
Test:
class OEmbedTest extends TestCase
{
public function tearDown()
{
Mockery::close();
}
/**
* It can return an OEmbed object
* #test
*/
public function it_can_return_an_o_embed_object()
{
$url = 'https://www.youtube.com/watch?v=9hUIxyE2Ns8';
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
$response = $this->postJson(route('oembed', ['url' => $url]));
$response->assertSuccessful();
}
}
Controller:
public function __invoke(Request $request)
{
$info = Embed::create($request->url);
$providers = $info->getProviders();
$oembed = $providers['oembed'];
return response()
->json($oembed
->getBag()
->getAll());
}
It seems you are mocking the Embed class the wrong way. If you use the Laravel facade method shouldReceive() instead of creating a Mock of the class itself, the framework will place the mock in the service container for you:
Embed::shouldReceive('create')
->with($url)
->once();
instead of
Mockery::mock(Embed::class)
->shouldReceive('create')
->with($url)
->once();
Also be aware that if the parameters your tested code passes to the mock differs from what you learned the mock with with($url), the mock considers itself uncalled. But you'll receive another error for calling a not defined method anyway.
I was able to solve this by using this in my test:
protected function setUp()
{
parent::setUp();
app()->instance(Embed::class, new FakeEmbed);
}
Then resolving it like this
$embed = resolve(Embed::class);
$embed = $embed->create($url);

How to write unit test cases for session variables in laravel 5.4

Hello I have a test case which will call a route and it will return some data if the session will set.
Here is my test case
class TestControllerTest extends TestCase
{
// ...
public function testResponseOfJson()
{
$response = $this->call('GET', 'profile/test');
$this->assertEmpty( !$response );
}
// ...
}
and here is my controller
Class TestController{
public function sendResponse(Request $request){
$this->data['user_id'] = $this->request->session()->get('userdata.userid');
if($this->data['userid']){
return data;
}
else{
return Failed;
}
}
}
My routes.php
Route::get('profile/test',['uses'=>'TestController#sendResponse']);
how can i set the session variable userdata.userid and get while doing unit testing.
Please check this page.
Laravel gives you the capability of using withSession chain method and other functions that will help you test where a session is required or needs to be manipulated in some way.
Example:
<?php
class ExampleTest extends TestCase
{
public function testApplication()
{
$response = $this->withSession(['foo' => 'bar'])
->get('/');
}
}

Call a method from one controller inside another

Is it possible to call a method from one controller inside another controller in Laravel 5 (regardless the http method used to access each method)?
This is how I have done it. Use the use keyword to make the OtherController available. Then you can call a method from that class on instantiation.
<?php namespace App\Http\Controllers;
use App\Http\Controllers\OtherController;
class MyController extends Controller {
public function __construct()
{
//Calling a method that is from the OtherController
$result = (new OtherController)->method();
}
}
Also check out the concept of a Command in Laravel. It might give you more flexibility than the method above.
use App\Http\Controllers\TargetsController;
// this controller contains a function to call
class OrganizationController extends Controller {
public function createHolidays() {
// first create the reference of this controller
$b = new TargetsController();
$mob = 9898989898;
$msg = "i am ready to send a msg";
// parameter will be same
$result = $b->mytesting($msg, $mob);
log::info('my testing function call with return value' . $result);
}
}
// this controller calls it
class TargetsController extends Controller {
public function mytesting($msg, $mob) {
log::info('my testing function call');
log::info('my mob:-' . $mob . 'my msg:-' . $msg);
$a = 10;
return $a;
}
}

Call method from (custom) controller class in Magento

I'm working with the M2e extension for Magento. Now I want to call a method of the class Ess_M2ePro_Adminhtml_ListingController in the file app/code/community/Ess/M2ePro/controllers/Adminhtml/ListingController.php.
But I don't know, how. I can't create an object or model to get access to the class to use the methods. Maybe it's not a good idea to call this controller methods directly, but in my case (remove a associated magento product to an ebay listing) it's required to call this methods.
In general these actions are called from the magento backend. I've also tried to create an admin_html session, but at the moment I don't have any further ideas.
Here's an example, how it looks like. I'm working with regular PHP-code, nothing special:
class Ess_M2ePro_Adminhtml_ListingController extends Ess_M2ePro_Controller_Adminhtml_MainController
{
//#############################################
protected function _initAction()
{
/** removed **/
}
protected function _isAllowed()
{
return Mage::getSingleton('admin/session')->isAllowed('m2epro/listings/listing');
}
//#############################################
public function indexAction()
{
/** removed **/
}
//#############################################
public function searchAction()
{
/** removed **/
}
public function searchGridAction()
{
/** removed **/
}
public function lockListingNowAction()
{
$listingId = (int)$this->getRequest()->getParam('id');
$component = $this->getRequest()->getParam('component');
$lockItemParams = array(
'id' => $listingId,
'component' => $component
);
$lockItem = Mage::getModel('M2ePro/Listing_LockItem',$lockItemParams);
if (!$lockItem->isExist()) {
$lockItem->create();
}
exit();
}
}
And I'm looking for something like this:
$test = Mage::getModel('M2ePro/Ess_M2ePro_Adminhtml_ListingController')->lockListingNowAction();
You shouldn't call methods from an other controller. Specially in your case, when you have exit at the end of the method.
You can use the _forward method if you are in a controller:
$this->_forward($action = 'lockListingNowAction', $controller = 'adminhtml_listing', $module = 'M2ePro', $params = array('id'=>$id)) //controller name may be different
But the cleanest way is to have the code you need in a helper and call the code from that helper in both controllers.

Resources