I have a problem with testing controllers in Laravel 4.
I have the next code:
public function getRemind()
{
$status = \Session::get('status');
$error = \Session::get('error');
$email = \Session::get('email');
return \View::make('admin/reminds/remind_form', compact('status', 'error', 'email'));
}
And I want to test if correct data passed in views by controller:
public function testGetRemind()
{
\Session::set('status', 'status');
\Session::set('error', 'error');
\Session::set('email', 'email');
$response = $this->action('GET', 'Admin\RemindersController#getRemind');
$this->assertTrue($response->isOk(), 'Get remind action is not ok');
$this->assertViewHas('status', 'status');
$this->assertViewHas('error', 'error');
$this->assertViewHas('email', 'email');
}
But this doesn't work.
Also I can't mock Session-class, because it not allowed by framework - there are a lot of errors when I try doing it.
Call Session::start() at the start of your test. When you call an URL in a test, the session is started if it has not already been started, wiping any existing data put into it.
Since v4.1.23, you can also do $this->session($arrayOfSessionData), which handles the starting of the session for you.
Related
I have to test the routes of a controller which uses Guzzle Wrapper as a client to get data from an API. The app uses Laravel as a framework.
This is the part of the code that gives me trouble in the controller's function I am currently testing:
public function addContacts(Request $request)
{
...
$client = new GuzzleWrapper();
$response = $client->post($uri, $data);
if($response->getStatusCode() != 200) {
return response()->json("Problem getting data", 500);
}
...
}
Now, what I have tried are
getMockBuilder:
$mock = getMockBuilder(GuzzleWrapper::class)->onlyMethods(array('post'))->getMock();
$mock->expects($this->once())->method('post')->willReturn(response()->json([$responseData], 200));
Http::fake :
Http::fake(
[$uri => Http::response([$responseData], 200)
);
Mockery :
$mockGuzzleClient = Mockery::mock(GuzzleWrapper::class);
$mockGuzzleClient->shouldReceive('post')
->andReturn(response()->json([$responseData], 200));
I also tried Mockery like this:
$mockGuzzleClient = Mockery::mock(GuzzleWrapper::class, function (MockInterface $mock){
$mock->shouldReceive('post')
->andReturn(response()->json([$responseData], 200));
});
And like this:
$this->app->instance(
GuzzleWrapper::class,
Mockery::mock(GuzzleWrapper::class, function (MockInterface $mock){
$mock->shouldReceive('post')
->andReturn(response()->json([$responseData], 200));
})
);
and following everything I tried is the call to test my controller's function:
//Successfully add contacts to list
$this->json('POST', $addContactUri, $input_data, $token);
$this->seeStatusCode(201);
Now! Whatever I tried, it's as if the GuzzleWrapper is never mocked, it still does the post and doesn't return status code 200. No matter what I find on google, it never fits with this scenario... Can anyone help me?
Mocking is based on the container, for Laravel to pick up your mocked classes, you should never use the new keyword. Instead use the container by using resolve().
$client = resolve(GuzzleWrapper::class);
This should work with one of the following mock approach where you use Mockery::mock(). But the way you are mocking the response, without seing the GuzzleWrapper, i would not expect it to return a Laravel response or else it is custom code.
$mockGuzzleClient = Mockery::mock(GuzzleWrapper::class, function (MockInterface $mock){
$mock->shouldReceive('post')
->andReturn(response()->json([$responseData], 200));
});
The most correct way would to use the Http facade. Your call in the controller should look like this.
Http::post($uri, $data);
The mocking should look like this, in general it seems like you are combining guzzle and Http interchangeable in your mocking attempts and that wont fly. If you mock with Http use the Http facade.
Http::fake([
$uri => Http::response(['your data' => 'response'], 200, []),
]);
What if you convert the GuzzleWrapper to a dependency? What I mean is to declare the GuzzleWrapper as the private property of the Controller, this way, it would be easier to test the controller: you need to pass the proper GuzzleWrapper to the constructor.
class ContactController {
private GuzzleWrapper $client;
public function __construct(GuzzleWrapper $client)
{
$this->client = $client;
}
public function addContacts(Request $request)
{
...
$response = $this->client->post($uri, $data);
if($response->getStatusCode() != 200) {
return response()->json("Problem getting data", 500);
}
...
}
}
Once you have done this, it should be easier to test. Then, if you need to test the client, I would recommend either the PHPUnit Mocks or Mockery.
If you can avoid testing the client (maybe you already tested it properly in other places), then I would recommend looking at my composer package https://packagist.org/packages/doppiogancio/mocked-client.
Simple enough situation, a user can edit their own profile. Run it through a browser test, works like a charm. Make a test for it... well now we have something interesting. I posted this over on the Laracasts site, but I figure more exposure is always good.
/** #test */
function auth_user_can_edit_own_profile()
{
$user = factory('App\User')->create();
$user->setProfile(); //this just makes an empty profile so a 1-1 relation exists
$this->actingAs($user);
$response = $this->post('profile/'.$user->id.'/update',[
'quote'=>'Hello World',
]);
$response->assertSee('Hello World');
}
I get a failure. what displays on the screen is a redirect to the main page. O.o Nothing in my code should make that happen.
I believe it has something to do with my form request's authorize() method. However, I fail to see how. I have tried using $this->withoutExceptionHandling() in my test with no change.
storeProfile form request:
public function authorize()
{
$profile = $this->route('profile');
if ($this->user()->can('edit profile') || $this->user()->id == $profile->user_id)
{
return true;
}
return false;
}
Profile Controller update:
public function update(storeProfile $request, Profile $profile)
{
$profile->update($request->except('name'));
$profile->save();
if($profile->user->name != $request->input('name'))
{
$profile->user()->update(['name' => $request->input('name')]);
//TODO event to track name changes
}
return redirect()->action('ProfileController#show',['id' => $profile->user_id]);
}
Any insights to this would be really helpful, but I truly don't understand what is happening. Thank you all for taking the time to read and help!
Things I have tried:
Set authroize to return true. - no change
$this->flushSession(); - no change
$this->withoutExceptionHandling() - nochange
I am learning Laravel 5.4, Still new to Laravel and PHPUnit. Everything is working great after following online basic tutorial.
This test function is working correctly when run phpunit
public function testBasicExample()
{
$response = $this->call( 'GET' , '/welcome');
$this->assertTrue(strpos($response->getContent(), 'Laravel') !== false);
}
Problem comes when I try to test Api
Steps I took
Create Api route for books
Return all users from users talbe as json from localhost/api/books/
public function index()
{
$users = DB::table('users')->get()->toJson();
echo $users;
}
I open the link in browser and json is returned correctly
copy and pasted json into online json validator jsonlint and it is valid.
Create a new test function
public function test_index_method_returns_all_books()
{
$response = $this->call( 'GET' , '/api/books/');
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getContent(),true);
$this->assertJson($data);
}
run phpunit
200 status test passed but assertJson did not pass.
I tried to do var_dump for $response->getContent() and found out it return empty.
now I am not able to get getContent() for api/book/. Does anyone know if there is a solution for this?
Thanks.
Here is a screenshot
Try create some data with a factory before you call the api:
e.g.:
factory(\App\Books::class, 20)->create();
then
$response = $this->call( 'GET' , '/api/books/');
If you had set ":memory:" as your database on phpunit.xml, you will no longer see any data from your local database, that's why you should use factory instead.
currently I have the following set up, a route that is calling a function in my controller that is in turn queuing a job.
//My Route
Route::get('/testJob', 'Controller#testJob');
//My Controller
public function testJob()
{
$job = (new testJob())->delay(5);
$this->dispatch($job);
}
//My job
public function handle()
{
require 'testAPICall.php';
// echo $response;
return $response;
}
//testAPICall.php
$response = 'this is the response';
//Queue After
Queue::after(function (JobProcessed $event) {
echo var_dump($event->data);
});
What I would like to be able to do, is access the response returned by the job in Queue::after, or alternatively, pass a callback into the queue to be execute after the job, again with access to the response from the job.
Is this something that is possible with Laravel Queues, and if so how would I go about this?
Cheers, Jack.
Queue::after() is a global callback, that will run after each job. So this might not what you want.
In your case, I would depend on Events/Listeners to be triggered after finishing the job.
public function handle(Mailer $mailer)
{
//Your code
event(new JobDone($data));
}
Please let me know if you need more details for implementation.
I have done something like yours that log a message "queue.txt" in laravel 5 "app" folder
This code I've got from a youtube video and not my code , but I have tested it successfully
First thing you have to code in "Routes.php" as below
Route::get('/',function()
{
//$queue = Queue::push('LogMessage',array('message'=>'Time: '.time()));
$queue = Queue::later(20,'LogMessage',array('message'=>'Time: '.time()));
return $queue;
});
class LogMessage{
public function fire($job,$data){
File::append(app_path().'/queue.txt',$data['message'].PHP_EOL);
$job->delete();
}
}
Then you can run your project folder using "php -S localhost:8888 -t public"
at the same time you must open a another terminal window in windows or linux environment and pointed to same folder and issue the command "php artisan queue:listen"
I think this will be helpful for you!
I am intentionally making error i.e. using one of the coulmn's name wrong to learn how to handle the error with ajax call with codeigniter.
Last Comment in controller function at the end is my question/problem
My AJX Code is following. I am using ajaxform plugin. It shows me all response from controller perfectly but problem is only I am unable to get response from model in controller while using ajax call that is little weird
$('#myForm').ajaxForm
({
success: function(responseText)
{
if(responseText != "1")
$('#feedback').html(responseText);
}
});
Following is snapshot of exact error which i can see in console but unable to get in controller and hence in view. It describes complete error i.e unknown column in given query, but i captured only upper portion.
My model function is below
public function updateItem($id, $data, $tbl)
{
$this->db->where('id', $id);
$r = $this->db->update($tbl, $data);
if($r)
return $r;
else
{
$r = $this->db->_error_message();
return $r;
}
}
My controller function code
public function upadteme()
{
$r = $this->ajax_model->updateItem($uid, $data, 'users');
echo $r." -- "; // Unable to get this echo working
//when using ajaxcall (calling controller through ajax) otherwise fine
}
It looks like the class will not populate _error_message if db_debug is on which it appears to be by default.
In config/database.php, set
$db['default']['db_debug'] = FALSE;
and try again.