I am receiving this error:
Call to undefined method Tests\Feature\ExampleTest::visit()
while running my test cases. I am a bit new to TDD.
Here is my ExampleTest Code
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function test_example()
{
$response = $this->visit('/')->see('Laravel');
$response->assertStatus(200);
}
}
From the video tutorial that I am using to learn about TDD the code above runs well without any issue but when it comes to running the code on my side I am faced with error as shown below :
• Tests\Feature\ExampleTest > example
Error
Call to undefined method Tests\Feature\ExampleTest::visit()
I am currently running Laravel 8.6 and PHPUnit 9.510
Any ideas on how I can resolve this are highly welcomed.
It looks like you are mixing up Laravel's built in browser testing methods (Dusk) - like visit() that you are using above - with the unit testing and feature testing methods.
As you are in the Tests\Feature namespace you need to follow the guide for unit and feature testing, the equivalent of which are:
public function test_example()
{
$response = $this->get('/');
$response->assertStatus(200);
}
Related
I am trying to use overload option of Mockery library on Laravel 5.
My current environment:
Laravel 5
Mockery 1.0
PHPUnit 7.5
I wrote this test case:
namespace Tests\Unit;
use Mockery;
use Tests\TestCase;
/**
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
*/
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock('overload:App\FooClass');
$mock->shouldReceive('callBar')
->times(2);
}
}
According to documentation, this test should fail, but does not matter what I do, the test never fails! It always result in:
Time: 304 ms, Memory: 19.53 MB
OK (1 test, 1 assertion)
If I remove the overload: option, the test fails. So I assume that I'm not using the library's methods as expected.
The new test:
namespace Tests\Unit;
use Mockery;
use Tests\TestCase;
/**
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
*/
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock('App\FooClass');
$mock->shouldReceive('callBar')
->times(2);
}
}
The result:
Mockery\Exception\InvalidCountException: Method callBar(<Any Arguments>) from Mockery_0__App_FooClass should be called exactly 2 times but called 0 times.
Am I doing anything wrong? Does anyone know how to use this option properly?
Reading the page, I think this is the error you are looking for:
When Mockery overloads a class, because of how PHP works with files,
that overloaded class file must not be included otherwise Mockery will
throw a “class already exists” exception. This is where autoloading
kicks in and makes our job a lot easier.
The error that you're looking for will be caused if you remove these 2 lines from your test, which are added to the code in the second code sample, and in the third sample on the manual page:
* #runTestsInSeparateProcesses
* #preserveGlobalState disabled
This means your code wont take advantage of the psr4 autoloader or any autoloader that is in place, and will create a new autoloader for you, at the expense of speed, since it wont be using your dumped classmap and has to build it up from scratch.
If you take the two lines above out, you will get the expected error, as it will try to overload your class with a class of the same name. That class will already be autoloaded, so you get a fatal error.
So if you want to block calls to callBar, and return void, that is what your code will be doing, which is correct.
Removing overload will mean your mock is no longer effective, as you will have to pass it through a constructor to get it to work.
Update:
With your update, I can conclude that your code must be running the mocked callBar method twice (not the actual callBar method), with your mock of fooBar class using overload. When the mocked method gets called, nothing inside the real callBar method actually happens, it just registers that it was called. If you're expecting it once for example, write shouldReceive(1) and then fix the code that your test runs.
When you remove the overload, the global injection doesnt take place, so your mock never gets injected. However, your mock callBar method on the mock class is still expecting to be ran twice, so you get the error. You will need to remove the 2 mock code lines completely from your test.
Keep the # statements in, as it will help prevent the psr4 error outlined above.
This is not the way to test, you should never use overload: option... Laravel helps you with a lot of things, don't create the wheel again or try to...
Why would you need to use #runTestsInSeparateProcesses and #preserveGlobalState disabled ?
Let's say your test is like this:
namespace Tests\Unit;
use App\FooClass;
use App\Service\ServiceForTesting;
use Mockery;
use Tests\TestCase;
class RenewSignatureTest extends TestCase
{
public function testHandle()
{
$mock = Mockery::mock(FooClass::class);
$mock->shouldReceive('callBar')
->times(2);
$myClass = app(ServiceForTesting::class);
$myClass->run($mock);
$myClass->run($mock);
}
}
You should have a code like this:
namespace App;
class FooClass
{
public function callBar()
{
return 'Works !!!';
}
}
Now, let's say you have a standalone class (no controller or similar, you are in a unit test), you should have something like this:
namespace App\Service;
use App\FooClass;
class ServiceForTesting
{
public function run(FooClass $class)
{
return $class->callBar();
}
}
I'm just getting started with the Laravel 8 testing suite and have opted to create a feature test for my account creation process. I've ran php artisan make:test AccountCreation and have written the first test case as a function, however, when I run php artisan test it's not picking up my feature tests, why?
Equally, if I try to delete the default example test, I get an error telling me that the test can't be found? What am I missing?
tests/Feature/AccountCreation.php
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class AccountCreation extends TestCase
{
/**
* A basic feature test example.
*
* #return void
*/
public function test_creates_user_account_successfully()
{
$response = $this->post('/api/account/create');
$response->assertStatus(201);
}
}
Is there a special command I need to run for Laravel to pick up these tests?
Because you should append 'Test' to your test class, because PHPUnit will check all classes end with Test, so change:
class AccountCreation extends TestCase { ...
to:
class AccountCreationTest extends TestCase { ...
Don't forget to change your class file name.
/** #test */
Put this before every test. It worked for me.
I am migrating our project to Symfony 4. In my test suites, we used PHPUnit for functional tests (I mean, we call endpoints and we check result). Often, we mock services to check different steps.
Since I migrated to Symfony 4, I am facing this issue: Symfony\Component\DependencyInjection\Exception\InvalidArgumentException: The "my.service" service is already initialized, you cannot replace it.
when we redefine it like this : static::$container->set("my.service", $mock);
Only for tests, how can I fix this issue?
Thank you
Replacing is deprecated since Symfony 3.3. Instead of replacing service you should try using aliases.
http://symfony.com/doc/current/service_container/alias_private.html
Also, you can try this approach:
$this->container->getDefinition('user.user_service')->setSynthetic(true);
before doing $container->set()
Replace Symfony service in tests for php 7.2
Finally, I found a solution. Maybe not the best, but, it's working:
I created another test container class and I override the services property using Reflection:
<?php
namespace My\Bundle\Test;
use Symfony\Bundle\FrameworkBundle\Test\TestContainer as BaseTestContainer;
class TestContainer extends BaseTestContainer
{
private $publicContainer;
public function set($id, $service)
{
$r = new \ReflectionObject($this->publicContainer);
$p = $r->getProperty('services');
$p->setAccessible(true);
$services = $p->getValue($this->publicContainer);
$services[$id] = $service;
$p->setValue($this->publicContainer, $services);
}
public function setPublicContainer($container)
{
$this->publicContainer = $container;
}
Kernel.php :
<?php
namespace App;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
public function getOriginalContainer()
{
if(!$this->container) {
parent::boot();
}
/** #var Container $container */
return $this->container;
}
public function getContainer()
{
if ($this->environment == 'prod') {
return parent::getContainer();
}
/** #var Container $container */
$container = $this->getOriginalContainer();
$testContainer = $container->get('my.test.service_container');
$testContainer->setPublicContainer($container);
return $testContainer;
}
It's really ugly, but it's working.
I've got a couple of tests like this (the real code performs some actions and returns a result, the test-version just returns false for every answer).
If you create and use a custom config for each environment (eg: a services_test.yaml, or in Symfony4 probably tests/services.yaml), and first have it include dev/services.yaml, but then override the service you want, the last definition will be used.
app/config/services_test.yml:
imports:
- { resource: services.yml }
App\BotDetector\BotDetectable: '#App\BotDetector\BotDetectorNeverBot'
# in the top-level 'live/prod' config this would be
# App\BotDetector\BotDetectable: '#App\BotDetector\BotDetector'
Here, I'm using an Interface as a service-name, but it will do the same with '#service.name' style as well.
As I understood it, it means that class X was already injected(because of some other dependency) somewhere before your code tries to overwrite it with self::$container->set(X:class, $someMock).
If you on Symfony 3.4 and below you can ovverride services in container regardless it privite or public. Only deprication notice will be emmited, with content similar to error message from question.
On Symfony 4.0 error from the question was thown.
But on Symfony 4.1 and above you can lean on special "test" container. To learn how to use it consider follow next links:
https://symfony.com/blog/new-in-symfony-4-1-simpler-service-testing
https://dev.to/nikolastojilj12/symfony-5-mocking-private-autowired-services-in-controller-functional-tests-24j4
I am new to Symfony, I have wrote small app now have to add unit tests, here is my controller:
<?php
namespace myBundle\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
class IndexController extends AbstractController
{
/**
* #param \Symfony\Component\HttpFoundation\Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
if ($this->getRequest()->isMethod('POST')) {
// Do Something
}
// Do Something otherwise
}
}
My test:
class IndexControllerTest extends \PHPUnit_Framework_TestCase
{
protected $testController;
public function setUp()
{
$this->testController =
$this->getMockBuilder('myBundle\Controller\IndexController')
->disableOriginalConstructor()
->getMock();
}
public function testPostSaveActionWithBadRequest()
{
$expectation = 'Some text ';
$response = $this->testController->indexAction(new Request);
$this->assertInstanceOf(
'Symfony\Component\HttpFoundation\JsonResponse',
$response
);
$content = json_decode($response->getContent());
$this->assertEquals($expectation, $content->error);
}
}
When I run this test I get following:
PHP Fatal error: Call to a member function get()
which is basically on following line
if ($this->getRequest()->isMethod('POST')) {
this tells me the container is null (I verified it by printing dump of the container).
any idea what am I missing here or is there a way to provide container as dependency for that test.
I really appreciate all the help.
thanks
FI
You're trying to mock the class you're suppose to test:
$this->testController =
$this->getMockBuilder('myBundle\Controller\IndexController')
->disableOriginalConstructor()
->getMock();
You should actually instantiate the class you're testing, and mock or stub its collaborators.
However, in this particular scenario, instead of writing a unit test, write a functional test. There's a chapter on writing functional tests in Symfony docs that'll help you.
Your controller uses lots of framework classes (classes that don't belong to you), and you shouldn't mock them either. That's why functional tests are better in this case. Also, make sure you move as much code as possible out of your controller, so you can properly unit test that part (and write as little functional tests as possible).
In the meantime read some books on unit testing (in the following order):
TDD by Example
Growing Object-Oriented Software Guided by Tests
Strange problem,
I have controller which uses \Symfony\Component\DependencyInjection\ContainerAwareTrait
class MainController
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
* #Route("/", name="_index")
* #Template()
*/
public function indexAction()
{
var_dump($this->container);
return array();
}
}
but result is NULL.
Tried on:
Symfony 2.5.*
MAMP 3.0
PHP 5.4 5.5
My searches have not helped me. I think the solution is easy.
Any ideas how to trace this error?
UPD: When i extend from Controller, container is available and everything is working properly. But according to symfony Controller reference extending is optional, i can use traits instead.
I'll venture a guess based on a quick glance into the Symfony source code: You still need to declare that you adhere to the ContainerAwareInterface Interface.
This is what the code looks like whenever Symfony is setting a container on a controller.
if ($controller instanceof ContainerAwareInterface) {
$controller->setContainer($this->container);
}
So then I suppose you need to do something like this:
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
// ...
class MainController implements ContainerAwareInterface
{
use ContainerAwareTrait;
/**
* #Route("/", name="_index")
* #Template()
*/
public function indexAction()
{
var_dump($this->container);
return array();
}
}
As an aside, this is arguably a pretty good case for Duck Typing, particularly if they had named the method something a bit more specific or if it were cheaper to inspect the parameter types to methods at runtime