I've been trying to test a model in a Symfony2 project, but I don't know how to get the entity manager to save and retrive records.
Can anyone point me to the right docs for this?
In order to test your models, you can use setUp() method. link to docs
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class MyModelTest extends WebTestCase
{
/**
* #var EntityManager
*/
private $_em;
protected function setUp()
{
$kernel = static::createKernel();
$kernel->boot();
$this->_em = $kernel->getContainer()->get('doctrine.orm.entity_manager');
$this->_em->beginTransaction();
}
/**
* Rollback changes.
*/
public function tearDown()
{
$this->_em->rollback();
}
public function testSomething()
{
$user = $this->_em->getRepository('MyAppMyBundle:MyModel')->find(1);
}
Hope this helps you
Symfony2 models are expected to be domain objects that represent domain models in the code.
domain objects should be defined purely to implement the business
behavior of the corresponding domain concept, rather than be defined
by the requirements of a more specific technology framework. -- Domain-driven design - Wikipedia, the free encyclopedia
Domain objects (and its tests) should not depend on Symfony2 APIs and Doctrine APIs except if you really want to test themselves.
Writing Symfony2 unit tests is no different than writing standard PHPUnit unit tests. -- Symfony - Testing
You can test business logic (processes, rules, behaviors, etc.) represented in domain objects with PHPUnit (or Behat) and usually test doubles.
namespace Ibw\JobeetBundle\Tests\Repository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Input\ArrayInput;
use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand;
use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\CreateSchemaDoctrineCommand;
class CategoryRepositoryTest extends WebTestCase
{
private $em;
private $application;
public function setUp()
{
static::$kernel = static::createKernel();
static::$kernel->boot();
$this->application = new Application(static::$kernel);
// drop the database
$command = new DropDatabaseDoctrineCommand();
$this->application->add($command);
$input = new ArrayInput(array(
'command' => 'doctrine:database:drop',
'--force' => true
));
$command->run($input, new NullOutput());
// we have to close the connection after dropping the database so we don't get "No database selected" error
$connection = $this->application->getKernel()->getContainer()->get('doctrine')->getConnection();
if ($connection->isConnected()) {
$connection->close();
}
// create the database
$command = new CreateDatabaseDoctrineCommand();
$this->application->add($command);
$input = new ArrayInput(array(
'command' => 'doctrine:database:create',
));
$command->run($input, new NullOutput());
// create schema
$command = new CreateSchemaDoctrineCommand();
$this->application->add($command);
$input = new ArrayInput(array(
'command' => 'doctrine:schema:create',
));
$command->run($input, new NullOutput());
// get the Entity Manager
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager();
// load fixtures
$client = static::createClient();
$loader = new \Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader($client->getContainer());
$loader->loadFromDirectory(static::$kernel->locateResource('#IbwJobeetBundle/DataFixtures/ORM'));
$purger = new \Doctrine\Common\DataFixtures\Purger\ORMPurger($this->em);
$executor = new \Doctrine\Common\DataFixtures\Executor\ORMExecutor($this->em, $purger);
$executor->execute($loader->getFixtures());
}
public function testFunction()
{
// here you test save any object or test insert any object
}
protected function tearDown()
{
parent::tearDown();
$this->em->close();
}
}
like in this Link : Jobeet Unit Test Tutorial
explain how to Test Entity and Entity Repository
Related
I'm running Symfony 5.4 and PHPUnit 9.5. I have a test which extends Symfony\Bundle\FrameworkBundle\Test\WebTestCase. I create an entity in my test, then execute the code under test. However, in my app's code, the entity is not to be found. I've tried finding the entity directly in the called app code and using dd() to dump it out (ending the test early), but I always get null. Somehow my test is using a different entity manager from the app code. This is how I'm fetching the entity manager:
<?php
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class MyTest extends WebTestCase {
protected EntityManager $entity_manager;
protected function setUp(): void {
$this->entity_manager = static::getContainer()->get('doctrine')->getManager();
}
public function testShouldCreateAnEntityThatIsVisibleInTheAppCode() {
$user = new User();
$user->setFirstName('Joe');
$user->setLastName('Bloggs');
$user->setEmail('joe.bloggs#example.com');
$this->entity_manager->persist($user);
$this->entity_manager->flush();
$crawler = static::$client->request('GET', 'https://localhost/admin/show-users');
$this->assertStringContainsString('joe.bloggs#example.com', $crawler->html());
}
}
How do I get my test to use the same entity manager as the code under test?
It turned out all I needed to do was add
static::$client->disableReboot();
into setUp() like so:
protected function setUp(): void {
static::$client->disableReboot();
$this->entity_manager = static::getContainer()->get('doctrine')->getManager();
}
A functional test class relies on an object reference created in a fixture. The reference's id, however, is not identical to the object's id property as returned by the entity manager. Below is a test that demonstrates this problem.
Notes:
The error is the same when using $this->setReference(...) as when
using the public const ... and $this->addReference(...).
The object reference used in the test appears to be the next
available id for nonprofit entities.
The test class was created after the error was observed in a more general test class.
The error is the same whether or not the fixtures are loaded before
running the test class.
The application uses Symfony 5.1.2 with all dependencies updated.
Test class:
namespace App\Tests\Controller;
use Liip\TestFixturesBundle\Test\FixturesTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ReferenceTest extends WebTestCase
{
use FixturesTrait;
public function setup(): void {
$this->client = $this->createClient();
$this->fixtures = $this->loadFixtures([
'App\DataFixtures\Test\OptionsFixture',
'App\DataFixtures\Test\NonprofitFixture',
'App\DataFixtures\Test\OpportunityFixture',
'App\DataFixtures\Test\UserFixture',
])
->getReferenceRepository();
$this->client->followRedirects();
$kernel = self::bootKernel();
$this->entityManager = $kernel->getContainer()
->get('doctrine')
->getManager('test');
}
public function testNonprofitReference() {
$npo = $this->entityManager->getRepository(\App\Entity\Nonprofit::class)
->findOneBy(['orgname' => 'Marmot Fund']);
$nId = $npo->getId();
$id = $this->fixtures->getReference('npo')->getId();
$this->assertEquals($nId, $id, 'Reference incorrect');
}
}
Test result:
Reference incorrect
Failed asserting that 4 matches expected 1.
NonprofitFixture (other fixtures may not be relevant):
namespace App\DataFixtures\Test;
use App\Entity\Nonprofit;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Bundle\FixturesBundle\ORMFixtureInterface;
class NonprofitFixture extends AbstractFixture implements OrderedFixtureInterface, ORMFixtureInterface
{
public const NPO_REFERENCE = 'npo';
public function load(ObjectManager $manager) {
$npo = new Nonprofit();
$npo->setOrgname('Marmot Fund');
$npo->setEin('123456789');
$npo->setActive(false);
// $this->setReference('npo', $npo);
$this->addReference(self::NPO_REFERENCE, $npo);
$npo1 = new Nonprofit();
$npo1->setOrgname('Turkey Fund');
$npo1->setEin('321654978');
$npo1->setActive(true);
$npo1->setWebsite('http://turkeysRUs.bogus.info');
$npo3 = new Nonprofit();
$npo3->setOrgname('Talk Trash Fund');
$npo3->setEin('978654321');
$npo3->setActive(true);
$npo3->setWebsite('http://ttrash.bogus.info');
$staff = $this->getReference(UserFixture::STAFF_REFERENCE);
$npo->setStaff($staff);
$opp = $this->getReference(OpportunityFixture::OPP_REFERENCE);
$opp1 = $this->getReference(OpportunityFixture::OPP1_REFERENCE);
$npo1->addOpportunity($opp);
$npo3->addOpportunity($opp1);
$manager->persist($npo);
$manager->persist($npo1);
$manager->persist($npo3);
$manager->flush();
}
public function getOrder() {
return 5; // the order in which fixtures will be loaded
}
}
framework.yaml excerpt:
liip_test_fixtures:
keep_database_and_schema: true
cache_db:
sqlite: liip_test_fixtures.services_database_backup.sqlite
dama_doctrine_test_bundle.yaml:
dama_doctrine_test:
enable_static_connection: true
enable_static_meta_data_cache: true
enable_static_query_cache: true
csv export from app.db:
"id","orgName"
"1","Marmot Fund"
"2","Turkey Fund"
"3","Talk Trash Fund"
The answer, such as it is, is that references have no place in a functional test. Their use is really a shortcut for clicking on links or taking some other action. A better test is to use the crawler to mimic the action.
I'm trying to do a unit test for a signup method and im following this guide. I'm fairly new to unit testing.
i keep getting
1) App\Tests\Controller\SignUpControllerTest::testSignUp Error: Cannot
instantiate interface
Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface
/Applications/MAMP/htdocs/my_project/tests/Controller/SignUpControllerTest.php:19
I just don't think im doing this unit test right. Here is what i have. I'm not sure on what im doing. All i want to do is test the signup method.
UserController.php
public function signup(Request $request, UserPasswordEncoderInterface $passwordEncoder )
{
$user = new User();
$entityManager = $this->getDoctrine()->getManager();
$user->setEmail($request->get('email'));
$user->setPlainPassword($request->get('password'));
$user->setUsername($request->get('username'));
$password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$entityManager->persist($user);
$entityManager->flush();
return $this->redirectToRoute('login');
}
SignUpControllerTest.php
namespace App\Tests\Controller;
use App\Entity\Product;
use App\Controller\UserController;
use App\Entity\User;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class SignUpControllerTest extends WebTestCase
{
public function testSignUp()
{
$passwordEncoder = new UserPasswordEncoderInterface();
$user = new User();
$user->setEmail('janedoe123#aol.com');
$user->setPlainPassword('owlhunter');
$user->setUsername('BarnMan');
$password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$userRepository = $this->createMock(ObjectRepository::class);
$userRepository->expects($this->any())
->method('find')
->willReturn($user);
$objectManager = $this->createMock(ObjectManager::class);
// use getMock() on PHPUnit 5.3 or below
// $objectManager = $this->getMock(ObjectManager::class);
$objectManager->expects($this->any())
->method('getRepository')
->willReturn($userRepository);
$userController = new UserController($objectManager);
$this->assertEquals(2100, $userController->signupTest());
}
}
The error is very clear. In the first line of your testSignUp method, you are creating an instance out of an interface, which cannot be done in PHP.
To create a usable object out of an interface in unit testing, create a mock object of it. Read PHP unit docs for that.
WebTestCase :
Application tests are PHP files that typically live in the tests/Controller/ directory of your application it tell Symfony that we need the tools necessary to talk to our application as if we were doing so via HTTP.
KernelTest
use for to fetch a service from the dependency injection container it help you creating and booting the kernel in your tests
that's why I recommend you to use KernelTestCase
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class SignUpControllerTest extends KernelTestCase
{
private $passwordEncoder;
protected function testSignUp(): void
{
self::bootKernel();
$this->passwordEncoder= self::getContainer()->get('Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface');
}
public function addShoes3()
{
$passwordEncoder = $this->passwordEncoder;
//continue
}
}
I am new to unit testing and trying to test a controller method in Laravel 5.1 and Mockery.
I am trying to test a registerEmail method I wrote, below:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Response;
use Mailchimp;
use Validator;
/**
* Class ApiController
* #package App\Http\Controllers
*/
class ApiController extends Controller
{
protected $mailchimpListId = null;
protected $mailchimp = null;
public function __construct(Mailchimp $mailchimp)
{
$this->mailchimp = $mailchimp;
$this->mailchimpListId = env('MAILCHIMP_LIST_ID');
}
/**
* #param Request $request
* #return \Illuminate\Http\JsonResponse
*/
public function registerEmail(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
]);
$email = $request->get('email');
try {
$subscribed = $this->mailchimp->lists->subscribe($this->mailchimpListId, [ 'email' => $email ]);
//var_dump($subscribed);
} catch (\Mailchimp_List_AlreadySubscribed $e) {
return Response::json([ 'mailchimpListAlreadySubscribed' => $e->getMessage() ], 422);
} catch (\Mailchimp_Error $e) {
return Response::json([ 'mailchimpError' => $e->getMessage() ], 422);
}
return Response::json([ 'success' => true ]);
}
}
I am attempting to mock the Mailchimp object to work in this situation.
So far, my test looks as follows:
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class HomeRouteTest extends TestCase
{
use WithoutMiddleware;
public function testMailchimpReturnsDuplicate() {
$listMock = Mockery::mock('Mailchimp_Lists')
->shouldReceive('subscribe')
->once()
->andThrow(\Mailchimp_List_AlreadySubscribed::class);
$mailchimp = Mockery::mock('Mailchimp')->lists = $listMock;
$this->post('/api/register-email', ['email'=>'duplicate#email.com'])->assertJson(
'{"mailchimpListAlreadySubscribed": "duplicate#email.com is already subscribed to the list."}'
);
}
}
I have phpUnit returning a failed test.
HomeRouteTest::testMailchimpReturnsDuplicate
Mockery\Exception\InvalidCountException: Method subscribe() from Mockery_0_Mailchimp_Lists should be called exactly 1 times but called 0 times.
Also, if I assert the status code is 422, phpUnit reports it is receiving a status code 200.
It works fine when I test it manually, but I imagine I am overlooking something fairly easy.
I managed to solve it myself. I eventually moved the subscribe into a seperate Job class, and was able to test that be redefining the Mailchimp class in the test file.
class Mailchimp {
public $lists;
public function __construct($lists) {
$this->lists = $lists;
}
}
class Mailchimp_List_AlreadySubscribed extends Exception {}
And one test
public function testSubscribeToMailchimp() {
// create job
$subscriber = factory(App\Models\Subscriber::class)->create();
$job = new App\Jobs\SubscribeToList($subscriber);
// set up Mailchimp mock
$lists = Mockery::mock()
->shouldReceive('subscribe')
->once()
->andReturn(true)
->getMock();
$mailchimp = new Mailchimp($lists);
// handle job
$job->handle($mailchimp);
// subscriber should be marked subscribed
$this->assertTrue($subscriber->subscribed);
}
Mockery will expect the class being passed in to the controller be a mock object as you can see here in their docs:
class Temperature
{
public function __construct($service)
{
$this->_service = $service;
}
}
Unit Test
$service = m::mock('service');
$service->shouldReceive('readTemp')->times(3)->andReturn(10, 12, 14);
$temperature = new Temperature($service);
In laravel IoC it autoloads the classes and injects them, but since its not autoloading Mailchimp_Lists class it won't be a mock object. Mailchimp is requiring the class atop it's main class require_once 'Mailchimp/Lists.php';
Then Mailchimp is then loading the class automatically in the constructor
$this->lists = new Mailchimp_Lists($this);
I don't think you'll be able to mock that class very easily out of the box. Since there isn't away to pass in the mock object to Mailchimp class and have it replace the instance of the real Mailchimp_Lists
I see you are trying to overwrite the lists member variable with a new Mock before you call the controller. Are you certain that the lists object is being replaced with you mocked one? Try seeing what the classes are in the controller when it gets loaded and see if it is in fact getting overridden.
Thus far I've been testing my ZF2 controllers as follows:
namespace Application\Controller;
use Application\Controller\IndexController;
use Zend\Http\Request;
use Zend\Http\Response;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router\RouteMatch;
use PHPUnit_Framework_TestCase;
class IndexControllerTest extends PHPUnit_Framework_TestCase
{
public function testIndexActionCanBeAccessed()
{
$this->routeMatch->setParam('action', 'index');
$result = $this->controller->dispatch($this->request);
$response = $this->controller->getResponse();
$this->assertEquals(200, $response->getStatusCode());
$this->assertInstanceOf('Zend\View\Model\ViewModel', $result);
}
protected function setUp()
{
\Zend\Mvc\Application::init(include 'config/application.config.php');
$this->controller = new IndexController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'index'));
$this->event = new MvcEvent();
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
}
protected $controller = null;
protected $event = null;
protected $request = null;
protected $response = null;
protected $routeMatch = null;
}
This allows me to test that the ViewModel is having the correct data (if any) assigned to it before the view is rendered. This serves that purpose just fine, but what it doesn't do is test that my routing is working correctly like the ZF1 Zend_Test_PHPUnit_Controller_TestCase tests would.
In those, I'd kick off the test by running $this->dispatch('/some/relative/url') and only get positive test results if the routes were set up correctly. With these ZF2 tests, I'm specifically telling it which route to use, which doesn't necessarily mean that a real request will be routed correctly.
How do I test that my routing is working correctly in ZF2?
I'm late to the party, but it still could be useful for newcomers. The solution nowadays would be to inherit from \Zend\Test\PHPUnit\Controller\AbstractControllerTestCase, so the usage would be very similar to ZF1:
class IndexControllerTest extends \Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase
{
public function setUp()
{
$this->setApplicationConfig(
include __DIR__ . '/../../../../../config/application.config.php'
);
parent::setUp();
}
public function testIndexActionCanBeAccessed()
{
$this->dispatch('/');
$this->assertResponseStatusCode(200);
$this->assertModuleName('application');
$this->assertControllerName('application\controller\index');
$this->assertControllerClass('IndexController');
$this->assertMatchedRouteName('home');
$this->assertQuery('html > head');
}
}
Note: This uses \Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase which includes assertQuery($path) as well as other web related methods.
EDIT: ZF2 has been updated since I self-answered this. PowerKiki's answer is better.