I'm writing the API test suite for a project I'm currently working on.
I can successfully run the suite using
codecept run api
and all the tests are passing successfully, but whenever I try running a single test using
codecept run api Tests\Users\Get
the following error is thrown:
PHP Fatal error: Class 'Tests\ApiCest' not found in /Users/Username/Programming/PHP/Project/tests/api/Tests/Users/GetCest.php on line 12
This is the folder structure I'm using:
The ApiCest.php is only used to quickly bootstrap the other tests. Here is its content:
namespace Tests;
/*
* This class is extended by the API tests.
*/
use ApiTester;
class ApiCest
{
public function _before(ApiTester $I)
{
$I->prepareRequest();
}
public function _after(ApiTester $I)
{
// All API responses must be in a JSON format.
$I->seeResponseIsJson();
}
}
And this is how it's used in the GetCest.php:
namespace Tests\Users;
use ApiTester;
use Codeception\Util\HttpCode;
use Tests\ApiCest;
/*
* Tests for the GET /users API endpoint.
*/
class GetCest extends ApiCest
{
// Tests methods are here but omitted
}
It happens because Tests\Users\GetCest class extends Tests\ApiCest class, but autoloading is not set-up for test classes.
Add this block to your composer.json file:
"autoload":{
"psr-4":{
"Tests\\": "tests/api/Tests"
}
}
If you already have autoload psr-4 block, add just "Tests\\": "tests/api/Tests" line to it.
Related
I'm trying to build a application that uses laravel. There is a test directory and I use it for application testing.
But I have an service in app/MyApp/Service directory. Here is the directory structure.
app/MyApp/Service
|-->Services
|-->Interfaces
|-->Test
|------->OLSServiceTest.php
Here is OLSServiceTest.php content
<?php
namespace App\MyApp\Service\Test;
class OLSServiceTest extends TestCase
{
public function testInfo()
{
$this->assertTrue(true);
}
public function testLogin()
{
$this->assertTrue(false);
}
/**
* Creates the application.
*
* Needs to be implemented by subclasses.
*
* #return \Symfony\Component\HttpKernel\HttpKernelInterface
*/
public function createApplication()
{
// TODO: Implement createApplication() method.
}
}
Then I'm trying to run the test manually
$ vendor/phpunit/phpunit/phpunit App\\MyApp\\Service\\Test\\OLSServiceTest
But getting this error :
[Symfony\Component\Debug\Exception\FatalErrorException]
Cannot declare class App\MyApp\Service\Test\OLSServiceTest, because
the name is already in use
Fatal error: Cannot declare class
App\MyApp\Service\Test\OLSServiceTest, because the name is already in
use in /var/www/app/MyApp/Service/Test/OLSServiceTest.php on line 29
What I am doing wrong?
Seems like you are trying to run test with wrong command. Try to use PHPUnit from Vendor bin directory I think it will solve your issue.
vendor/bin/phpunit app/MyApp/Service/Test/OLSServiceTest.php
I'm using the Laravel framework and have a directory of the following structure:
models
Statistic.php
presenters
StatisticPresenter.php
In my models/Statistic class, I'm trying to access a present method which calls a protected property which then references my presenter:
protected $presenter = 'presenters\StatisticPresenter';
public function present() {
return new $this->presenter($this);
}
Yet, this returns the error: Class 'presenters\StatisticPresenter' not found. I realize this is a namespacing error of some sort, and I've tried watching a few videos on how it works, but I simply can't wrap my head around it. I have already added my presenter folder to my composer.json file. For example, adding this to the top of my Statistic model does not work:
use presenters\StatisticPresenter;
How can I fix this?
Do the followings;
Mark your namespace in StatisticPresenter.php ? (at the top of file "namespace presenters;")
Add PSR-4 class map to your composer
{
"autoload": {
"psr-4": {
"presenters\\": "app/presenters/"
}
}
}
run "composer dump-autoload" once and you wont need to run this command again for the "presenters" namespace if you add new classes into "app/presenters/ folder"
Test your class with "use presenters/StatisticPresenter;"
If you can access your class you dont need to change your code your present() function will be valid
I've been comparing my code to this question and many other guides online but with little success. Everything works fine until I try to inject the the interface in my controller. When I do inject it, I get the Target Interface is not instantiable error.
app\models\Interfaces\InterviewInterface.php
<?php namespace app\models\Interfaces;
interface InterviewInterface {
public function fetch($id);
}
app\models\Interview.php
use app\models\Interfaces\InterviewInterface;
class Interview extends Eloquent implements InterviewInterface {
public function fetch($id)
{
return Interview::find($id);
}
}
routes.php
App::bind('app\models\Interfaces\InterviewInterface');
composer.json
"psr-4": {
"Blog\\Articles\\" : "app/lib/Blog/Articles",
"app\\models\\Interfaces\\" : "app/models/Interfaces/InterviewInterface.php"
}
AdminInterviewController.php
use app\models\Interfaces\InterviewInterface as InterviewInterface;
class AdminInterviewController extends BaseController {
public function __construct(InterviewInterface $interview)
{
$this->interview = $interview;
$this->beforeFilter('auth');
}
}
As soon as I add the
use app\models\Interfaces\InterviewInterface as InterviewInterface;
and
__construct(InterviewInterface $interview)
$this->interview = $interview;
lines, it gives me the error. I pull them out, no error.
I have run php composer.phar dump-autoload and php artisan dump-autoload commands multiple times and they succeed.
Any ideas? I really appreciate it.
You need to bind the interface to a class in order to resolve it from the IOC:
In routes.php, assuming it is not namespaced:
App::bind('app\modesl\Interfaces\InterviewInterface', 'Interview');
I have something like the following set up in Laravel:
In /app/controllers/MyController.php:
class MyController extends BaseController {
const MAX_FILE_SIZE = 10000;
// ....
}
In /app/tests/MyControllerTest.php:
class MyControllerTest extends TestCase {
public function myDataProvider() {
return [
[ MyController::MAX_FILE_SIZE ]
];
}
/**
* #dataProvider myDataProvider
*/
public function testMyController($a) {
// Just an example
$this->assertTrue(1 == 1);
}
}
However, when I run vendor/bin/phpunit I get the following error:
PHP Fatal error: Class 'Controller' not found in /home/me/my-app/app/controllers/BaseController.php on line 3
Fatal error: Class 'Controller' not found in /home/me/my-app/app/controllers/BaseController.php on line 3
If I remove the reference to the MyController class in myDataProvider() and replace it with a literal constant then the test completes successfully.
In addition, I can place references to MyController::MAX_FILE_SIZE inside the actual testMyController() method, and the test also completes successfully.
It appears that the autoloading setup for Laravel framework classes isn't being set up until after the data provider method is being called, but before the actual test methods are called. Is there any way around this so that I can access Laravel framework classes from within a PHPUnit data provider?
NOTE: I'm calling PHPUnit directly from the command line and not from within an IDE (such as NetBeans). I know some people have had issues with that, but I don't think that applies to my problem.
As implied in this answer, this appears to be related to the order that PHPUnit will call any data providers and the setUp() method in any test cases.
PHPUnit will call the data provider methods before running any tests. Before each test it will also call the setUp() method in the test case. Laravel hooks into the setUp() method to call $this->createApplication() which will add the controller classes to the 'include path' so that they can be autoloaded correctly.
Since the data provider methods are run before this happens then any references to controller classes inside a data provider fail. It's possible work around this by modifying the test class to something like this:
class MyControllerTest extends TestCase {
public function __construct($name = null, array $data = array(), $dataName = '') {
parent::__construct($name, $data, $dataName);
$this->createApplication();
}
public function myDataProvider() {
return [
[ MyController::MAX_FILE_SIZE ]
];
}
/**
* #dataProvider myDataProvider
*/
public function testMyController($a) {
// Just an example
$this->assertTrue(1 == 1);
}
}
This will call createApplication() before the data provider methods are run, and so there is a valid application instance that will allow the appropriate classes to be autoloaded correctly.
This seems to work, but I'm not sure if it's the best solution, or if it is likely to cause any issues (although I can't think of any reasons why it should).
The test will initialize much faster if you create the application right within the dataProvider method, especially if you have large set of items to test.
public function myDataProvider() {
$this->createApplication();
return [
[ MyController::MAX_FILE_SIZE ]
];
}
Performance warning for the other solutions (especially if you plan to use factories inside your dataProviders):
As this article explains:
The test runner builds a test suite by scanning all of your test
directories […] When a
#dataProvider annotation is found, the referenced data provider is
EXECUTED, then a TestCase is created and added to the TestSuite for
each dataset in the provider.
[…]
if you use factory methods in your data providers, these
factories will run once for each test utilizing this data provider
BEFORE your first test even runs. So a data provider […] that is used by ten tests
will run ten times before your first
test even runs. This could drastically slow down the time until your
first test executes. Even […] using phpunit --filter,
every data provider will still run multiple times. Filtering occurs after the test
suite has been generated and therefore after any
data providers have been executed.
The above article proposes to return a closure from the dataProvider and execute that in your test:
/**
* #test
* #dataProvider paymentProcessorProvider
*/
public function user_can_charge_an_amount($paymentProcessorProvider)
{
$paymentProcessorProvider();
$paymentProcessor = $this->app->make(PaymentProviderContract::class);
$paymentProcessor->charge(2000);
$this->assertEquals(2000, $paymentProcessor->totalCharges());
}
public function paymentProcessorProvider()
{
return [
'Braintree processor' => [function () {
$container = Container::getInstance();
$container->bind(PaymentProviderContract::class, BraintreeProvider::class);
}],
...
];
}
You can adjust this behaviour of PHPUnit by adding your custom bootstrapper to your projects phpunit.xml like this (look at 3rd line):
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="tests/bootstrap.php" ← ← ← THIS
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
...
</phpunit>
Then create a bootstrap.php file in your tests folder (i.e. the path you denoted above), and paste this:
<?php
use Illuminate\Contracts\Console\Kernel;
require __DIR__ . '/../vendor/autoload.php';
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
You can now use Laravel functionality in your data providers, just keep in mind they still run after your setUp methods.
I'm running PHPUnit using a bootstrap file for autoloading classes (generated by composer).
All my tests load up classes just fine, but for two of my tests, I made a "base" test class which extends \PHPUnit\Framework\TestCase (similar to PHPUnit_Framework_TestCase before PHPUnit7), and then two test classes that extend the base class, similar structure to the following example code:
abstract class BaseTest extends \PHPUnit\Framework\TestCase
{
abstract function setUp();
protected function getCommonTestVariables()
{
// ...
}
protected function runCommonTests()
{
// ...
}
}
class BlahBlahTest extends BaseTest
{
public function setUp()
{
$variables=$this->getCommonTestVariables();
//etc...
}
public function testThings()
{
$this->runCommonTests();
}
}
Whenever I run this, PHPUnit gives an error:
Fatal error: Class 'BaseTest' not found in BlahBlahTest.php on line 13
I've checked filenames, locations, namespaces and everything seems to be in order. Any help would be appreciated to get to the bottom of this
I ran into the same problem and if you are not too familiar with the inner workings of both PHPUnit and Composer this can indeed seem perplexing.
PHPunit does not use use the Composer autoloader to find any of your test classes. It just scans any directory you give it and operates on one file at a time.
Hence it does not know about any other class than the one in the file it is currently operating on. That is where the bootstrap file comes into play.
If you want to use the Composer Autoloader to load other test classes, you need to tell it where it can find these test classes (and, optionally, in which namespace).
There are two ways to do this:
Add an autoload-dev section to your composer.json or
Add the test directory to the Composer Autoloader
Use autoload-dev
The autoload-dev sections allows you to define autoload rules for development purposes.
Quoting directly from the manual:
Classes needed to run the test suite should not be included in the
main autoload rules to avoid polluting the autoloader in production
and when other people use your package as a dependency.
Therefore, it is a good idea to rely on a dedicated path for your unit
tests and to add it within the autoload-dev section.
Example:
{
"autoload": {
"psr-4": { "MyLibrary\\": "src/" }
},
"autoload-dev": {
"psr-4": { "MyLibrary\\Tests\\": "tests/" }
}
}
Add to the Composer Autoloader
An alternative would be to get the Composer Autoloader and add your testing namespace (if you have any) and the directory where your tests live. How to do this, as described in the manual (at the bottom of the autoloading section in "Basic Usage") is :
$loader = require('/path/to/vendor/autoload.php');
$loader->add('Test\\', __DIR__ . '/Tests');
If your tests use namespaces that mirror the test directory and you still run into trouble, you can try omitting the prefix by replacing the first parameter ('Test\\') with ''.
If you want further insight into how all of this works you should take a look at the Composer ClassLoader class, especially the add() and findFile() methods.
For me the solution was much simpler.
I changed the capital letter of Test to test at the end of the file and the class name
BaseSomethingtest.php
<?php
namespace Something\Tests\Sub1\Sub2;
class BaseSomethingtest
{
}
I just put a word at the end of my base class. As far as it
didn't finish with Test, phpunit didn't call it
BaseSomethingTestCase.php
<?php
namespace Something\Tests\Sub1\Sub2;
class BaseSomethingTestCase
{
}
I'm not sure if you still need a solution, but this worked for me:
Non-testable base class extending PHPUnit_Framework_TestCase
In PHP 7.0+ extends PHPUnit_Framework_TestCasechanged to extends \PHPUnit\Framework\TestCase, try this one.