I am a beginner in Symfony 2.8. I have a problem with my controller.
That is my controller:
class ExampleController extends ExtraController
{
/**
* #ParamConverter("site", class="Bundle:Site", converter="site_slug_converter")
* #Route("/formacion-example", name="example_web.front.example_training", requirements={"site": "es"})
*
* Render the Example form page
*
* #param Site $site
*
* #return Response
*/
public function example2TrainingFormAction(Site $site)
{
$options = ['site' => $site, 'projectId' => $this->get('example.doctrine.project_getter')->getProject()];
$form = $this->createForm(ExampleTrainingType::class, null, $options);
$viewData = ['form' => $form->createView()];
return $this->render('ExampleFrontContactFormBundle:Example:example_training.html.twig', $viewData);
}
}
When I go to my Route www.example.com/es/formacion-example symfony return to me:
HTTP status: Error 500
Controller: n/a
Route name:example_web.front.example_training
Has session: no
In symfony documentation I cant find a solution.
Thank you! :)
adding the answer here as well:
i.e. the site parameter was missing from the route
#Route("/{site}/formacion-example", ...
Related
I write here after many attempts but my problem isn't solved yet.
I want to create a test using PHPUnit on Laravel my class has function described like below:
public function test_not_connected_user_can_not_create_new_task() {
$this->withoutExceptionHandling();
//Given we have a task object
$task = Task::factory()->make();
// When unauthenticated user submits post request to create task endpoint
// He should be redirected to login page
$this->post('/tasks/store',$task->toArray())
->assertRedirect(route('login'));
}
Here is my route:
Route::post('/tasks/store', [App\Http\Controllers\TaskController::class, 'store'])
->name('store');
And my controller functions:
public function __construct() {
$this->middleware('auth')->except(['index','show']);
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request) {
$task = Task::create([
'title' => $request->get('title'),
'description' => $request->get('description'),
'user_id' => Auth::id()
]);
return redirect()->route('show', [$task->id]);
}
We have a middleware here to manage authentication.
When I run the test:
vendor/bin/phpunit --filter test_connected_user_can_create_new_task
I get this error:
Tests\Feature\TasksTest::test_not_connected_user_can_not_create_new_task
Illuminate\Auth\AuthenticationException: Unauthenticated.
And it's pointing to this line:
$this->post('/tasks/store', $task->toArray())
The expected behavior is that it should redirect to login but here the test fail and I can't see why.
Thanks
The issue is super easy to fix. You have $this->withoutExceptionHandling(); and that is literally throwing the error, what you want is to catch it and let everything continue. To do so, remove that line of code and your test will work.
I want to test the next method of my controller
function index(){
if(Auth::User()->can('view_roles'))
{
$roles = Role::all();
return response()->json(['data' => $roles], 200);
}
return response()->json(['Not_authorized'], 401);
}
it is already configured for authentication (tymondesigns / jwt-auth) and the management of roles (spatie / laravel-permission), testing with postman works, I just want to do it in an automated way.
This is the test code, if I remove the conditional function of the controller the TEST passes, but I would like to do a test using a user but I have no idea how to do it.
public function testIndexRole()
{
$this->json('GET', '/role')->seeJson([
'name' => 'admin',
'name' => 'secratary'
]);
}
Depends on what kind of app are you building.
A - Using Laravel for the entire app
If your using Laravel for frontend/backend, well to simulate a logged-in user you could use the awesome Laravel Dusk package, made by the Laravel team. You can check the documentation here.
This package has some helpful methods to mock login sessions amongs a lot more of other things, you can use:
$this->browse(function ($first, $second) {
$first->loginAs(User::find(1))
->visit('/home');
});
That way you hit an endpoint with a logged-in user of id=1. And a lot more of stuff.
B - Using Laravel as a backend
Now, this is mainly how I use Laravel.
To identify a user that hits an endpoint, the request must send an access_token. This token helps your app to identify the user. So, you will need to make and API call to that endpoint attaching the token.
I made a couple of helper functions to simply reuse this in every Test class. I wrote a Utils trait that is being used in the TestCase.php and given this class is extended by the rest of the Test classes it will be available everywhere.
1. Create the helper methods.
path/to/your/project/ tests/Utils.php
Trait Utils {
/**
* Make an API call as a User
*
* #param $user
* #param $method
* #param $uri
* #param array $data
* #param array $headers
* #return TestResponse
*/
protected function apiAs($user, $method, $uri, array $data = [], array $headers = []): TestResponse
{
$headers = array_merge([
'Authorization' => 'Bearer ' . \JWTAuth::fromUser($user),
'Accept' => 'application/json'
], $headers);
return $this->api($method, $uri, $data, $headers);
}
protected function api($method, $uri, array $data = [], array $headers = [])
{
return $this->json($method, $uri, $data, $headers);
}
}
2. Make them available.
Then in your TestCase.php use the trait:
path/to/your/project/tests/TestCase.php
abstract class TestCase extends BaseTestCase
{
use CreatesApplication, Utils; // <-- note `Utils`
// the rest of the code
3. Use them.
So now you can do API calls from your test methods:
/**
* #test
* Test for: Role index
*/
public function a_test_for_role_index()
{
/** Given a registered user */
$user = factory(User::class)->create(['name' => 'John Doe']);
/** When the user makes the request */
$response = $this->apiAs($user,'GET', '/role');
/** Then he should see the data */
$response
->assertStatus(200)
->assertJsonFragment(['name' => 'admin'])
->assertJsonFragment(['name' => 'secretary']);
}
Side note
check that on top of the test methods there is a #test annotation, this indicates Laravel that the method is a test. You can do this or prefix your tests names with test_
I need to configure my providers dynamically.
$config = [
'client_id' = 'xxxxxxx',
'client_token' = 'xxxxxxx',
'redirect' = 'http://example.com/'
];
return Socialite::with($provider)->setConfig($config)->redirect();
But unfortunately there is no function setConfig.
I need to set provider, client_id, client_secret and redirect dynamically
Is there any ideas?
Thank you!
You could use the Socialite buildProvider method like:
$config = [
'client_id' = 'xxxxxxx',
'client_token' = 'xxxxxxx',
'redirect' = 'http://example.com/'
];
return Socialite::buildProvider(\Laravel\Socialite\Two\FacebookProvider::class, $config);
Where \Laravel\Socialite\Two\FacebookProvider::class would be swapped with your service (if different) as provided in either folder One/Two in https://github.com/laravel/socialite/tree/2.0/src
I use the following service provider in order to automatically fill in the redirect for each provider where it's empty.
It could be modified to update your configuration on the fly. It depends exactly what you're trying to do I suppose.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class SocialServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
collect(config('services'))
->only(config('social.providers'))
->reject(function($config) {
return array_get($config, 'redirect', false);
})
->each(function($config, $key) {
$url = url("login/{$key}/callback", [], true);
config(["services.{$key}.redirect" => $url]);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
}
}
This could help if anyone still faces the problem
you can set the Redirect Url manually
$driver = Socialite::driver('google');
$driver->redirectUrl('your-custom-url');
I have a controller that i am trying to do a functional test for it.
controller:
<?php
namespace Zanox\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Exception;
/**
*
* #author Mohamed Ragab Dahab <eng.mohamed.dahab#gmail.com>
*
* #Route("merchant")
*
*/
class ReportController extends Controller {
/**
* Show transaction report regarding to the given merchant ID
* #author Mohamed Ragab Dahab <eng.mohamed.dahab#gmail.com>
* #access public
*
* #Route("/{id}/report", name="merchant-report")
*
* #param int $id Merchant ID
*/
public function showAction($id) {
try {
//Order Service
$orderService = $this->get('zanox_app.orderService');
//merchant Orders
$orders = $orderService->getMerchantOrders($id);
//render view and pass orders array
return $this->render('ZanoxAppBundle:Report:show.html.twig', ['orders' => $orders]);
} catch (Exception $e) {
//log errors
}
}
}
I have created a functional test as following:
namespace Zanox\AppBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ReportControllerTest extends WebTestCase {
/**
*
*/
public function testShow() {
//Client instance
$client = static::createClient();
//Act like browsing the merchant listing page, via GET method
$crawler = $client->request('GET', '/merchant/{id}/report', ['id'=> 1]);
//Getting the response of the requested URL
$crawlerResponse = $client->getResponse();
//Assert that Page is loaded ok
$this->assertEquals(200, $crawlerResponse->getStatusCode());
//Assert that the response content contains 'Merchant Listing' text
$this->assertTrue($crawler->filter('html:contains("Merchant Report")')->count() > 0);
}
}
However this test fails as the first assertion returns status 500 instead of 200
Test log shows:
[2015-07-06 21:00:24] request.INFO: Matched route "merchant-report". {"route_parameters":{"_controller":"Zanox\AppBundle\Controller\ReportController::showAction","id":"{id}","_route":"merchant-report"},"request_uri":"http://localhost/merchant/{id}/report?id=1"} []
Letting you know that ['id' => 1] exists in DB.
First Question: why it fails?
Second Question: am i doing the functional test in a proper way?
If you look at the logs, you see that the {id} parameter is not correctly replaced but is added in the query string of your Uri. So try with:
$crawler = $client->request('GET', sprintf('/merchant/%d/report', 1));
When using GET, the third parameter will add query parameters for the URI, when using POST, these data will be posted.
As to why it fails - you can troubleshoot the problem by using a debugger to step through the controller code when it is executed in your test. For your second question, yes, you are doing a simple functional test correctly.
I`m trying to write some functional tests for a REST API, created using FOS Rest Bundle.
The problem is that when I use the Symfony\Component\BrowserKit, symfony throws me the following error:
{"message":"Unable to find template \"AccountBundle:Account:list.html.twig\". .. }
The code that I run is:
$client = static::createClient();
$client->request('GET','/account');
When I run the request from the browser, it works fine.
Here is the controller:
/**
* Get channel by ID
* #Secure(roles="ROLE_USER")
* #RestView()
* #ApiDoc(
* resource=true,
* description="Get channel by id",
* section="Channel",
* output="Channel"
* )
*/
public function getAction(Channel $channel)
{
return array('channel' => $channel);
}
So when in test scenario, instead of returning the JSON tries to load the template.
You should use the $server parameter of the $client-request() method to set the Accept header to application/json. FOSRestBundle has a listener that returns JSON only if the corresponding Accept header is received, otherwise it will search for the template corresponding to the controller.
$client->request('GET', '/account', array(), array(), array('HTTP_ACCEPT' => 'application/json'));