how to write a phptest for controller in fuelphp - php

I only have a controller and view, I want to mock a http request to test this controller, I use fuelphp, hope someone can give me some advice or demo
class Controller_Index extends Controller_Template{
public function action_index(){
$view = View::forge('index');
$this->template->content = $view;
}
}
I write like this
class Test_Controller_index extends TestCase{
public function TestController(){
$expected = View::forge('index');
$response = Request::forge('index')
->set_method('GET')
->execute()
->response();
$assertValue = $response->body->content;
$this->assertSame($expected, $assertValue);
}
}
php oil test result
There was 1 failure:
1) Warning
No tests found in class "Test_Controller_index".
what's wrong

All unit tests in fuelphp need to take the form of test_ or they won't be recognised.
Try this: (not the different function name)
class Test_Controller_index extends TestCase{
public function test_controller(){
$expected = View::forge('index');
$response = Request::forge('index')
->set_method('GET')
->execute()
->response();
$assertValue = $response->body->content;
$this->assertSame($expected, $assertValue);
}
}

Related

Laravel: Test not Using Mocked Method

I want getNumber to return 200 in the test case, but when I run the test and inspect the result with $response->dump() I can see that the mock is not overriding the method as it is showing array:1 [0 => 100]. What am I missing?
Controller:
class ServiceController extends Controller
{
public function fetch(Request $request)
{
$service = new Service();
return [$service->getNumber()];
}
}
Model:
class Service extends Model
{
public function getNumber(): int
{
return 100;
}
}
Test:
class ControllerTest extends TestCase
{
public function testFetchGetsNumber()
{
$mock = $this->partialMock(Service::class, function (MockInterface $mock) {
$mock->shouldReceive('getNumber')->andReturn(200);
});
$response = $this->post('/api/controller');
$response->dump(); // returns 100 but should return 200...
}
}
I am using Laravel 8 and referencing this documentation: https://laravel.com/docs/8.x/mocking#mocking-objects
In Laravel you have to use the container to mock classes.
$service = resolve(Service::class);
In constructors or handle methods, if you inject your classes there it will automatically use the container. If put into constructors or classes, remember the resolution of the class with the constructor, have to use the container.
class Job
{
public function handle(Service $service)
{
// will use the mocked service.
}
}

Call function in one controller to another controller

I am new to Laravel. I have some functions in PaymentController. I want to call those functions from SmartpaySController. Here is the function which is available in PaymentController. Help me to call that function by staying in SmartpaySController.
public function getPaymentFailed($paymentId) {
$transactionData = $this->paymentRepo->find($paymentId);
if($transactionData) {
$data['quote'] = $this->quoteRepo->getQuoteById($transactionData->quote_id);
$data['metaTitle'] = 'Payment failed';
$data['returnMessage'] = $transactionData->return_message;
return view('payment::payment.quote_payment_failed', $data);
}
}
Thank you.
Instead of calling controller methods, the better practice is that you can create traits like: app/Traits and extend in controller
//trait
trait traitName {
public function getData() {
// .....
}
}
//Controller
class ControlelrName extends Controller {
use TraitName;
}
I recomend you to not call functions from one controller to another.
Make Helpers, Resources or implement same feature in other way
Never use controllers as object
But if you want to do it anyway you can use:
SomeController.php
class SomeController extend Controller {
public function someFunction(Request $request) {
// Here Some Code
}
}
YourController.php
use SomeController;
...
public function getPaymentFailed(Request $request, $paymentId) {
$controller_data = (new SomeController)->someFunction($request);
$transactionData = $this->paymentRepo->find($paymentId);
if($transactionData) {
$data['quote'] = $this->quoteRepo->getQuoteById($transactionData->quote_id);
$data['metaTitle'] = 'Payment failed';
$data['returnMessage'] = $transactionData->return_message;
return view('payment::payment.quote_payment_failed', $data);
}
}
Change:
public function getPaymentFailed($paymentId)
to:
public static function getPaymentFailed($paymentId)
This will make it staticly available in your SmartpaySController by doing:
PaymentController::getPaymentFailed($paymentId);
You can make use of Real-Time Facades
Using real-time facades, you may treat any class in your application
as if it were a facade.
To generate a real-time facade, prefix the namespace of the imported
class with Facades:
//...
use use Facades\App\Http\Controllers\SomeController;
//...
return SomeController::getPaymentFailed($request, $paymentId);

How create return values when mocking Eloquent on Laravel

I am working on a project on Laravel, and I have developed my own version of User. I know that Laravel comes with it is own implementation, but just for the sake of the argument, let's forget that.
I am trying to learn how to test in Laravel, and I encountered the following problem:
Part of the controller
<?php
class UsersController extends BaseController {
protected $user;
public function __construct(User $user) {
$this->user = $user;
}
public function login() {
// Input, Validation, blah, blah blah
....
// Find user
$users = $this->user->where('email', '=', $email);
if ($users->count() == 0) {
// Do something
} else {
// Do something else different
}
// Lot more stuff...
}
}
Now comes testing. As you see I put the dependency of Eloquent through the constructor so I can mock it. The test are as follow:
<?php
class UsertTest extends TestCase {
public function __construct() {
$this->userMock = Mockery::mock('Eloquent', 'User');
}
public function tearDown() {
Mockery::close();
}
public function testLogin() {
// Unimportant
$data = ...
$this->userMock
->shouldReceive('where')
->once()
->andReturn('foo');
$this->app->instance('User', $this->userMock);
// Send the data
$this->post('/login', $data);
When I run the tests, I get this error
PHP Fatal error: Call to a member function count() on a non-object
So it seems that the mock is working, in the sense that the Database is not being hit, but now I am with the problem that I need to mock as well the value returned by the call.
How should I go about it?

testing php laravel controller shouldReceive arguments

I have a laravel model which uses ardent/eloquent. I am trying to set up tests for the controller in particular, storing a new model that uses the ardent model.
The method works in the app but I'm having trouble with my tests
I'm having problems working out how to mock the calls this method makes.
My controllers set up and the method in question is this one:
use golfmanager\service\creator\TicketCreatorInterface;
//controller manages the ticket books
class BooksController extends BaseController {
/**
* Book Repository
*
* #var Book
*/
protected $book;
protected $ticket;
public function __construct(Book $book, TicketCreatorInterface $ticket)
{
$this->book = $book;
$this->ticket = $ticket;
}
public function store()
{
$input = Input::all();
$result = $this->book->save();
if ($result) {
//if book created then create tickets
$this->ticket->createTicket($input, $this->book);
return Redirect::route('books.index');
}
return Redirect::route('books.create')
->withInput()
->withArdentErrors()
->with('message', 'There were validation errors.');
}
And the methods used by the interface (TicketCreator):
public function createTicket($input, $book) {
//dd($input);
$counter = $input['start_number'];
while($counter <= $input['end_number']) {
$ticketDetails = array(
'ticketnumber'=>$counter,
'status'=>'unused',
'active'=>1
);
$this->ticket->create($ticketDetails)->save();
$this->ticket->book()->associate($book)->save();
$counter = $counter+1;
}
return $counter;
}
My test is as follows:
use Mockery as m;
use Way\Tests\Factory;
class BooksTest extends TestCase {
public function __construct()
{
$this->mock = m::mock('Ardent', 'Book');
$this->collection = m::mock('Illuminate\Database\Eloquent\Collection')->shouldDeferMissing();
}
public function setUp()
{
parent::setUp();
$this->attributes = Factory::book(['id' => 1, 'assigned_date'=> '20/11/2013']);
$this->app->instance('Book', $this->mock);
}
public function testStore()
{
Input::replace($input = ['start_number'=>1000, 'end_number'=>1010, 'assigned_date'=>'20/11/2013']);
$this->mock->shouldReceive('save')->once()->andReturn(true);
$this->ticket->shouldReceive('createTicket')->once()->with($input, $this->mock)->andReturn(true);
//with($input);
//$this->validate(true);
$this->call('POST', 'books');
$this->assertRedirectedToRoute('books.index');
}
Currently I get an error:
No matching handler found for Book::save()
Is this being thrown because the book model doesnt contain a save method? If it is how do I mock the model correctly. I don't want it to touch the database (although it could if it has to).
Is it the multiple saves in the createTicket method?
Still learning how to set up tests correctly - slowly getting there but not enough knowledge yet.
If I change the name of the method in shouldReceive to say 'store' it still comes up with the save() error message.
Update:
I have isolated part of the problem to the createTicket call. I've changed my testStore test and updated as above.
My error with this current test is: Undefined index: start_number.
If I remove the call to createTicket in the controller method I don't get an error. I tried using Input::replace to replace the input from a form but appears not getting through to my function
How can I simulate a form input in the mocked objects?
Thanks

Cakephp call an component method inside a helper

I use Cakephp 2.1 and I need to call a component method which resides in a plugin, from a view helper:
The component is here:
/app/Plugin/Abc/Controller/Component/AbcComponent.php
The helper is here:
/app/View/Helper/SimpleHelper.php
I tried inside helper:
App::import('Component', 'Abc.Abc');
$this->Abc = new Abc(); or $this->Abc = new AbcComponent;
or
$this->Abc = $this->Components->load('Abc.Abc');
inside the controllers this component works with no problem.
I know this isn't recommended (MVC design etc.) but if I don't use it this way I need to duplicate a lot of code. I need to make something like:
MyHelper extends Helper{
$simpleVar = Component->get_data();
}
I use CakePHP 2.4
This is how I successfully call Component from a Helper:
App::uses('AclComponent', 'Controller/Component');
class MyHelper extends AppHelper {
public function myFunction() {
$collection = new ComponentCollection();
$acl = new AclComponent($collection);
// From here you can use AclComponent in $acl
if ($acl->check($aro, $aco) {
// ...
}
}
}
Passing data from CakePHP component to a helper
This seems to be a very nice way to handle this.
I tried working the way you are before, and, although it seems to be a nice immediate solution, in the long run, it is better to just work with the component and helper as 2 separate entities in your controller.
lee
You can put logic in trait and use this from component and helper, if your porpouse is to use the same business logic in different places, to avoid duplication code.
By example
the trait (file app/Lib/NameOfTrait.php or app/PluginName/Lib/NameOfTrait.php)
trait NameOfTrait {
public function theTraitFunc($a, $b) {
// Code here
}
}
The Component:
App::uses('Component', 'Controller');
App::uses('NameOfTrait', 'PluginName.Lib');
class NameOfComponent extends Component {
use NameOfTrait;
private $member;
private $controller;
public function __construct(ComponentCollection $collection, $settings = array()) {
parent::__construct($collection, $settings);
$this->member = $settings['memberName'];
}
function startup(Controller $controller) {
$this->controller = $controller;
}
/**
* Wrap function call of trait function,
* I think the function doesn't have the same name,
* I don't try this but I think is obvious,
* to avoid the function to call itself
*/
public function theTraitFuncWrap($a) {
return $this->theTraitFunc($a, $this->member);
}
}
Do The same for the Helper.
I hope this help someone, bye :)

Categories