consider the following:
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class HomeRouteTest extends TestCase
{
public function testVisitTheHomePage()
{
$response = $this->call('GET', '/');
$this->assertEquals(200, $response->status());
}
public function testVisitTheAboutPage()
{
$response = $this->call('GET', '/about');
$this->assertEquals(200, $response->status());
}
}
Is there away, not that I have seen documented >.>, to do something like:
$response = $this->call('GET', 'home.about');
$this->assertEquals(200, $response->status());
Or .... Is that how you do it?
The error I get is:
vagrant#scotchbox:/var/www$ phpunit
PHPUnit 4.8.21 by Sebastian Bergmann and contributors.
FF
Time: 3.41 seconds, Memory: 14.25Mb
There were 2 failures:
1) HomeRouteTest::testVisitTheHomePage
Failed asserting that 404 matches expected 200.
/var/www/tests/HomeRouteTest.php:12
2) HomeRouteTest::testVisitTheAboutPage
Failed asserting that 404 matches expected 200.
/var/www/tests/HomeRouteTest.php:19
FAILURES!
Tests: 2, Assertions: 2, Failures: 2.
This solution works for any Laravel 5 version to my knowledge. Especially Laravel 5.4+ unlike the other solution mentioned here.
If your named route has parameters, you can just do this:
$response = $this->get(route('users.show', [
'user' => 3,
]));
$response->assertStatus(200);
If your named route has no parameters then you can just do this:
$response = $this->get(route('users.index'));
$response->assertStatus(200);
Nice and simple.
This is a very late response, but I think what you're looking for is:
$this->route('GET', 'home.about');
$this->assertResponseOk(); // Checks that response status was 200
For routes with parameters, you can do this:
$this->route('GET', 'users.show', ['id' => 3]); // (goes to '/users/3')
I needed this to test some ghastly routes I'm working with, which look like this:
Route::resource(
'/accounts/{account_id}/users',
'AccountsController#users',
[
'parameters' => ['account_id'],
'names' => [
'create' => 'users.create',
'destroy' => 'users.destroy',
'edit' => 'users.edit',
'show ' => 'users.show',
'store' => 'users.store',
'update' => 'users.update',
]
]
);
To make sure that my routes and redirects went to the right page I did this:
/**
* #test
*/
public function users_index_route_loads_correct_page()
{
$account = Account::first();
$response = $this->route('get', 'users.index', ['account_id' => $account->id]);
$this->assertResponseOk()
->seePageIs('/accounts/' . $account->id . '/users');
}
To make sure I was using the right view file (we all make copy-paste errors, right?), I did this:
/**
* #test
*/
public function users_index_route_uses_expected_view()
{
$account = Account::first();
$response = $this->route('get', 'users.index', ['account_id' => $account->id]);
$view = $response->original; // returns View instance
$view_name = $view->getName();
$this->assertEquals($view_name, 'users.index');
}
If it weren't for the Structure feature in PHPStorm and the documentation from Laravel 4.2 that I stumbled over, I don't think I would have ever figured out how to test those routes. I hope this saves someone else a bit of time.
To be honest I don't see any reason to test names route. Named routes are rather for easier use inside application and you should rather test concrete url and not names route to make sure you are using correct url.
If you need to really test named route, I would create helper function for that with simple array:
protected function getNamedRoute($name)
{
$routes = [
'home.about' => '/about',
];
return $routes[$name];
}
and then in your test:
public function testVisitTheHomePage()
{
$response = $this->call('GET', $this->getNamedRoute('home.about'));
$this->assertEquals(200, $response->status());
}
The second thing is that you can make those tests a bit cleaner, you don't need to use:
$response = $this->call('GET', '/');
$this->assertEquals(200, $response->status());
you can use:
$this->visit('/')->seeStatusCode(200);
For me it's much more legible and you can write less code to achieve same.
Related
I am working on a SilverStripe project. I am trying to write functional tests for my application following this documentation, https://docs.silverstripe.org/en/4/developer_guides/testing/functional_testing/. I am testing a POST request. But it is not working. You can see my code below.
I have a controller class called CustomFormPageController with the following code.
class CustomFormPageController extends PageController
{
private static $allowed_actions = [
'testPostRequest',
];
private static $url_handlers = [
'testPostRequest' => 'testPostRequest',
];
public function testPostRequest(HTTPRequest $request)
{
if (! $request->isPOST()) {
return "Bad request";
}
return "Request successfully processed";
}
}
I also have a page class for that controller called, CustomFormPage. Following is the implementation of the class.
class CustomFormPage extends Page
{
}
What I am trying to test is that I am trying to test testPostRequest method returns the correct value.
Following is my test class
class CustomFormPageTest extends FunctionalTest
{
protected static $fixture_file = 'fixtures.yml';
public function testTestingPost()
{
$formPage = $this->objFromFixture(CustomFormPage::class, 'form_page');
$response = $this->post($formPage->URLSegment . '/testPostRequest', [
'name' => 'testing'
]);
var_dump($response);
}
}
Following is my fixtures.yml file.
SilverStripe\CMS\Model\SiteTree:
sitetree_page_one:
ID: 1
ClassName: CustomFormPage
Title: Page Title 1
URLSegment: custom-form-page
CustomFormPage:
form_page:
ID: 1
Title: Page Title 1
URLSegment: custom-form-page
When I run the test, it is always returning 404 not found status even though the page is created in the database. What is missing in my code and how can I fix it?
I just found the solution. We need to publish the page in the test as follows.
$formPage->publish("Stage", "Live");
I have the following notification class:
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class ConfirmEmailNotification extends Notification implements ShouldQueue
{
use Queueable;
public function __construct()
{
//
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
$user = $notifiable;
$url = url('/register/confirm/'. $user->confirmation_token);
return (new MailMessage)
->subject('Confirm Email')
->markdown('emails.confirm', ['user' => $user, 'url' => $url]);
}
public function toArray($notifiable)
{
return [
//
];
}
}
In my controller I have the following:
$when = now()->addSeconds(30);
$user->notify((new ConfirmEmailNotification())->delay($when));
But nothing is getting added to the queue table - the emails is being fired instantly?
I configured the queue as follows.
In my env file:
QUEUE_DRIVER=database
In my config/queue.php I have renamed the table as follows:
'database' => [
'driver' => 'database',
'table' => 'queued_jobs',
'queue' => 'default',
'retry_after' => 90,
],
Run the following:
php artisan queue:table
php artisan migrate
php artisan queue:work
I've tried php artisan config:clear but no difference.
Any ideas chaps?
Fixed by restarting php artisan serve
In my case, i forgot to update the QUEUE_CONNECTION property in .env file.
After updating the QUEUE_CONNECTION to database it worked as intended.
Got a couple of problems with my unit testing with Jasmine. First one:
I need to test this in a Component called CaseList:
gotoDetail(case: Case){
this._router.navigate(['CaseDetail', {"id": case.id}]);
}
All my attempts at the tests give the error is this._router is undefined, well that's because I haven't defined it in my test, as I can't figure out how! I haven't even come up with any good attempts at the tests, as I have no idea how to proceed. So that's why I haven't posted any attempt here...
Edit: The part in the router-test which is related to to the problem above, but I test all the routing in a separate file! This test works!
it('Should navigate to Case Detail List', (done) => {
router.navigate(['CaseDetail', {id: 'test'}]).then(() => {
expect(location.path()).toEqual('/casedetail/test');
done();
}).catch(e => done.fail(e));
});
Second tests from a Detail Component (where user is navigated after choosing case) :
addStep(){
this.case.getSteps().push(new Step());
}
I also have a remove method I need to test:
removeStep(step: Step){
this.case.removeStep(step);
}
Constructor for this component:
constructor(public _routeParams: RouteParams, public _service: Service) {
this.case = _service.getById(_routeParams.get('id'));
}
So the test I tried doing for the add-method:
it('passes new step to case-class', () => {
spyOn(case, 'addStep')
.and.returnValue(Observable.of({complete: true}))
caseDetail.addStep();
expect(case.addStep).toHaveBeenCalledWith(step);
});
So these methods call the methods that are in a separate class called "Case".
The error I'm getting when testing these are that case is null. I guess the routing and service messes it up, as in the same Component I have a other "identical" methods, and testing those works fine. But they belong to a different class.
Method in same component, referring to a "Step"-class:
addFeedback(step: Step){
step.addFeedback(new Feedback());
}
The testing works perfectly:
it('passes feedback value to Step class', () => {
spyOn(step, 'addFeedback')
.and.returnValue(Observable.of({complete: true}))
caseDetail.addFeedback(step);
expect(step.addFeedback).toHaveBeenCalledWith(feedback);
})
So obviously in testing the component I should have everything defined that is needed, since the testing of the feedback method works. I just need to define the "case" object somehow, so that it doesn't complain about it being null.
Well hopefully you get my problem at hand and hopefully you can help! :)
For your test cases to work, you will have to add router and case as a provider before the test.
Routing Example:
import {RootRouter} from 'angular2/src/router/router';
import {Location, RouteParams, Router, RouteRegistry, ROUTER_PRIMARY_COMPONENT} from 'angular2/router';
import {SpyLocation} from 'angular2/src/mock/location_mock';
import {provide} from 'angular2/core';
describe('Router', () => {
let location, router;
beforeEachProviders(() => [
RouteRegistry,
provide(Location, {useClass: SpyLocation}),
provide(Router, {useClass: RootRouter}),
provide(ROUTER_PRIMARY_COMPONENT, {useValue: App}),
]);
beforeEach(inject([Router, Location], (_router, _location) => {
router = _router;
location = _location;
}));
it('Should be able to navigate to Home', done => {
router.navigate(['Index']).then(() => {
expect(location.path()).toBe('');
done();
}).catch(e => done.fail(e));
});
});
Case Provider:
import {Case} from '../case';
beforeEachProviders(() => [
provide(case, Case)
]);
What am I missing here? Here is my controller code:
public function calculate() {
$this->set(array(
"route" => array("A" => 1, "B" => 2),
"_serialize" => array("route")
));
return;
}
Here is a line from my routes.php file:
Router::parseExtensions();
Here is my test code:
$result = $this->testAction("/itinerary/calculate.json", array(
"method" => "POST",
"return" => "contents"
));
This code throws
MissingViewException: View file "C:\xampp\htdocs\fiver\app\View\Itinerary\calculate.ctp" is missing.
I am obviously missing something here. Please help. Another test for another controller with JSON works just fine
Got it. CakePHP requires the RequestHandler component to be explicitly added to the controller for the extensions to work. I've added this line, it started to work
public $components = array('RequestHandler');
If you dont have/want a view for your controller you can simply add
$this->autoRender = false;
// EDIT: Only working if you dont want an output but thats not the case
I have a method in my Users Controller called view, which should display a specified (by URL) user:
public function view($username = null) {
$this->User->username = $username;
if (!$this->User->exists()) {
throw new NotFoundException('Няма такъв потребител!');
}
if (!$username) {
$this->Session->setFlash('Няма такъв потребител!');
$this->redirect(array('action' => 'index'));
}
$this->set('user', $this->User->read());
}
And in the route config:
Router::connect('/:username', array('controller' => 'users', 'action'=> 'view'), array('username' => '^([a-z0-9])+$'));
But when I try: www.example.com/Username it returns a fatal error: Missing controller.
I tried also this:
Router::connect('/users/:username', array('controller' => 'users', 'action'=> 'view'), array('pass' => array('username'), 'username' => '^([a-z0-9])+$'));
Unfortunately for this sort of setup using /:username is too simple, it will pretty much override every single other route. To do this effectively ( + the proper CakePHP way) you need to setup a custom route, here is mine which pretty much achieves the same thing. Just replace "product" with "user" for most cases, read through it though, to make sure you understand what's going on..
Look at my routes config as well if you still can't work it out.
If you want to route /user/dunhamzz to a profile you would set it up like this:
Router::connect('/user/:username',
array('controller' => 'users', 'action' => 'view'),
array('pass' => array('username')
);
Then your view action simply gets the username as the first argument:
public function view($username) {
}