I am using DataFixtures to populate my test db with data for unittests.
For entities, which are not using other entities I can set the primary key with setId. For entities, which are used by other entities I can set it, but it is ignored.
E.g. I am setting my users like this:
/** #var Gender $genderW */
$genderM = $this->getReference(GenderFixtures::TEST_GENDER_W);
$date = new DateTime('now');
/** #var User $user */
$user = new User();
$user
->setId(9)
->setFirstName('Hermione')
->setLastName('Granger')
->setEmail('test9#example.com')
->setGender($genderM)
->setPassword('odsf3_!45sr-f')
->setCreated($date);
$manager->persist($user);
$manager->flush();
$this->addReference(self::TEST_USER_REFERENCE_9, $user);
As you see I set the Id for Hermione, but if I do a var_dump in my tests I see that the user Id gets incremented by my number of users for each test I am running. (9, 18, 27....)
The result is that in my tests I have to fetch the users by the unique email address, which is possible, but very annoying:
$userRepo = $this->em->getRepository(User::class);
$this->user = $userRepo->findOneBy(['email' => 'test9#example.com']);
var_dump($this->user->getId());
Is there a possibility to change this, so I can get my users by Id?
config:
"require": {
"php": "7.2.*",
"ext-ctype": "*",
"ext-iconv": "*",
"cache/predis-adapter": "^1.0",
"doctrine/doctrine-bundle": "^1.6.10",
"doctrine/doctrine-migrations-bundle": "^1.3",
"doctrine/orm": "^2.5.11",
"eightpoints/guzzle-bundle": "~7.3.1",
"friendsofsymfony/rest-bundle": "^2.3",
"guzzlehttp/guzzle": "^6.3",
"jms/serializer-bundle": "^2.4",
"predis/predis": "^1.1",
"sensio/framework-extra-bundle": "^5.2",
"snc/redis-bundle": "3.x-dev",
"symfony/apache-pack": "^1.0",
"symfony/console": "^4.1",
"symfony/flex": "^1.0",
"symfony/framework-bundle": "^4.1",
"symfony/lts": "^4#dev",
"symfony/monolog-bundle": "^3.1",
"symfony/orm-pack": "^1.0",
"symfony/polyfill-apcu": "^1.5",
"symfony/security-bundle": "^4.0",
"symfony/swiftmailer-bundle": "^3.2",
"symfony/twig-bundle": "^4.1",
"symfony/validator": "^4.1",
"symfony/yaml": "^4.1"
},
"require-dev": {
"diablomedia/phpunit-pretty-printer": "2.0.*",
"doctrine/doctrine-fixtures-bundle": "^3.0",
"friendsofphp/php-cs-fixer": "*",
"phpmd/phpmd": "^2.6",
"sensiolabs/security-checker": "^4.1",
"squizlabs/php_codesniffer": "*",
"symfony/dotenv": "^4.1",
"symfony/maker-bundle": "^1.5",
"symfony/phpunit-bridge": "^4.1",
"symfony/var-dumper": "^4.1"
},
The correct way would be to create a setUp and tearDown Method in each class/for your Testsuite.
With these methods you can create/truncate the testdata in your database each time it is run.
According to the Phpunit documentation for Databasetesting the setup will clean up before the test is run
PHPUnit will execute a TRUNCATE against all the tables you specified to reset their status to empty.
With only a truncate, not resetting the autoincrement values.
To clean your database keys you should use the tearDown for the test to reset the keys
ALTER TABLE tablename AUTO_INCREMENT = 1
This will reset the autoincrement values after each test.
To achieve this only with symfony you could create a command which will do the following things:
Reset the testdatabase ./app/console doctrine:fixtures:load --purge-with-truncate found this command on SO
Create fixtures
Execute your testrun
You will have 1 command to run your tests only using symfony.
Related
I've succeeded to successfully construct a REST API using APEX language defined with an annotation: #RestResource.
I also wrote a matching Unit test procedure with #isTest annotation. The execution of the REST API triggered by a HTTP GET with two input parameters works well, while the Unit Test execution, returns a "null" value list resulting from the SOQL query shown below:
String mycase = inputs_case_number; // for ex. '00001026'
sObject[] sl2 = [SELECT Id, CaseNumber FROM Case WHERE CaseNumber = :mycase LIMIT 1];
The query returns:
VARIABLE_ASSIGNMENT [22]|sl2|[]|0x1ffefea6
I've also tried to execute it with a RunAs() method (see code below), using a dynamically created Salesforce test user, not anonymous, connected to a more powerful profile, but still receiving a "null" answer at the SOQL query. The new profile defines "View All" permission for Cases. Other SOQL queries to objects like: "User" and "UserRecordAccess" with very similar construction are working fine, both for REST APEX and Test APEX.
Is there a way to configure an access permission for Unit test (#isTest) to read the Case object and a few fields like: Id and CaseNumber. Is this error related to the "Tooling API" function and how can we fix this issue in the test procedure?
Code attachment: Unit Test Code
#isTest
private class MyRestResource1Test {
static testMethod void MyRestRequest() {
// generate temporary test user object and assign to running process
String uniqueUserName = 'standarduser' + DateTime.now().getTime() + '#testorg.com';
Profile p = [SELECT Id FROM Profile WHERE Name='StandardTestUser'];
User pu = new User(Alias='standt',Email='standarduser#testorg.com',LastName='testing',EmailEncodingKey='UTF-8',LanguageLocaleKey='en_US',LocaleSidKey='en_US',ProfileId=p.Id,TimeZoneSidKey='America/New_York',UserName=uniqueUserName);
System.RunAs(pu) {
RestRequest req = new RestRequest();
RestResponse res = new RestResponse();
req.requestURI = '/services/apexrest/sfcheckap/';
req.addParameter('useremail','testuserid#red.com');
req.addParameter('casenumber','00001026');
req.httpMethod = 'GET';
RestContext.request = req;
RestContext.response = res;
System.debug('Current User assigned is: ' + UserInfo.getUserName());
System.debug('Current Profile assigned is: ' + UserInfo.getProfileId());
Test.startTest();
Map<String, Boolean> resultMap = MyRestResource1.doGet();
Test.stopTest();
Boolean debugflag = resultMap.get('accessPermission');
String debugflagstr = String.valueOf(debugflag);
System.assert(debugflagstr.contains('true'));
}
}
}
Found a solution path by using: #isTest(SeeAllData=true)
See article: "Using the isTest(SeeAllData=true) Annotation"
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_seealldata_using.htm
I have a complex/nested object created by automatic hydration from Zend\Form data. Now I want to save it with Doctrine 2. The best case would be just one persist(...) and one flush(...) call on the top level. But it doesn't work like this. So now I have following problem:
There are objects User and Order. The relationship is 1:n (so, 1 User has n Orders). The User exists already. When a User Joe tries to save more than one Order (e.g. its second order), an error occurs:
A new entity was found through the relationship '...\Order#user' that was not configured to cascade persist operations for entity: ...\User#000000003ba4559d000000005be8d831. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement '...\User#__toString()' to get a clue.
Allright, I add cascade={"persist"} (though it doesn't make sense here, but anyway, just to try it out):
class Order
{
...
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User", cascade={"persist"})
*/
protected $user;
...
}
Now it works, if the given User doesn't exist: An Order and a User is created.
But if the User exists, an error occurs:
An exception occurred while executing 'INSERT INTO user (username, role, created, updated) VALUES (?, ?, ?, ?)' with params ["myusername", "member", null, null]:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'myusername' for key 'username_UNIQUE'
How to handle the saving so, that the User only gets saved, if it doesn't exist yet?
The solution is to persist the User before saving the referencing entity. If it doesn't exist yet, it needs to be created (persisted and flushed) first:
$user = $this->entityManager->getRepository(User::class)->findOneBy(
['username' => $dataObject->getUser()->getUsername()]
);
if (! $user) {
$this->entityManager->persist($dataObject->getUser());
$this->entityManager->flush($dataObject->getUser());
$user = $this->entityManager->getRepository(User::class)->findOneBy(
['username' => $dataObject->getUser()->getUsername()]
);
}
$dataObject->setUser($user);
$this->entityManager->persist($dataObject);
$this->entityManager->flush();
And the cascade={"persist"} should not be used, since it actually doesn't make sense in this case.
EDIT
Or even easier:
$user = $this->entityManager->getRepository(User::class)->findOneBy(
['username' => $dataObject->getUser()->getUsername()]
);
if ($user) {
$dataObject->setUser($user);
} else {
$this->entityManager->persist($dataObject->getUser());
}
$this->entityManager->persist($dataObject);
$this->entityManager->flush();
I am trying to test a ZF3 controller action which, in the process, selects a user from the database via a Doctrine ORM repository using a token given as a GET-Parameter. As I need to make sure that the User exists I need to create a mock of the repository returning the user object. How do I do this?
My setup is the following:
The class UserControllerFactory is instantiating a UserController class:
class UserControllerFactory implements FactoryInterface {
/**
* #param ContainerInterface $container Zend\ServiceManager\ServiceManager
* #param string $requestedName
* #param array|NULL $options
*
* #return UserController
*/
public function __invoke(ContainerInterface $container, $requestedName, Array $options = NULL) {
$entityManager = $container->get('doctrine.entitymanager.orm_default');
$userRepository = $entityManager->getRepository('User\Entity\User');
return new UserController($container, $entityManager, $userRepository);
}}
In the UserController the acton resetPassword is called. It gets the needed parameter from the route and selects a user from the database matching the token:
public function resetPasswordAction() {
$request = $this->getRequest();
$passwordResetToken = $this->params()->fromRoute('token');
if(strlen(trim($passwordResetToken))) {
$user = $this->userRepository->findOneBy(
[
'passwordResetToken' => $passwordResetToken
]
);
...
If no user is found. The action will redirect to user to a different action.
PHPUnit test case:
public function testResetPasswordActionCanBeAccessed() {
$passwordResetToken = 'testToken1234';
$this->dispatch("/user/resetPassword/$passwordResetToken", 'GET');
$this->assertNotRedirect();
}
As there is no user having the token is will be redirected.
To my knowledge I need to create a mock of the repository (userRepository), create a mock user and use the mock repository retrieve the mock user having the token.
I am not sure if this is the right approche as I tried a lot of tutorial and never got it to work. I don't know how to "replace" the, in the action called "userRepository" with the in the unit test created mock object.
I am happy to provide more details if needed.
EDIT
As suggested by #DonCallisto (thank you) I changed my test case code to:
...
$mockedEm = $this->createMock(EntityManager::class);
$mockedUserRepository = $this->createMock('Core\Repository\EntityRepository');
$mockedEm->method('getRepository')->willReturn($mockedUserRepository);
$mockedUserRepository->method('findOneBy')->willReturn($mockedUser);
$this->dispatch("/$this->_lang/user/resetPassword/$passwordResetToken", 'GET');
...
but after calling the "dispatch" in the test case my controller call
$user = $this->userRepository->findOneBy(...)
will still return NULL instead of the mocked user object given in the test. If I debug the $mockedUserRepository, my $mockedUser is assigned correctly.
I also tried the suggested:
$mockedUserRepository->findOneBy([arrayWithParams])->willReturn($mockedUser);
But this will through an error due to the fact that $mockedUserRepository->findOneBy() is returning NULL.
It worked some time ago but now for some reason it produces exception. I have custom User entity and it extends FOS user:
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
...
}
There is no setSalt() in my custom class. And as I can see in dumped SQL query other not custom fields (email_canonical, enabled, password, ...) are set properly. What else could I check?
UPDATE
I did composer update. Here is my composer.json
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.2.*",
"doctrine/orm": "^2.5",
"doctrine/doctrine-bundle": "^1.6",
"doctrine/doctrine-cache-bundle": "^1.2",
"symfony/swiftmailer-bundle": "^2.3",
"symfony/monolog-bundle": "^2.8",
"symfony/polyfill-apcu": "^1.0",
"sensio/distribution-bundle": "^5.0",
"sensio/framework-extra-bundle": "^3.0.2",
"incenteev/composer-parameter-handler": "^2.0",
"friendsofsymfony/user-bundle": "~2.0#dev",
"hwi/oauth-bundle": "^0.5.1",
"twig/extensions": "^1.4"
},
You must drop your schema and recreate it again. Your salt column should be allowed to be null, because when using the bcryp algorithm it is indeed null as the salt is directly included in the password (hash). Moreover here is the mapping declaration:
<field name="salt" column="salt" type="string" nullable="true" />
PS: And other advices, updates all bundle, clear the cache, the database and its data...
UPDATE:
Per dMedia, they changed the doctrine mapping in a recent update (Nov 2016)
https://github.com/FriendsOfSymfony/FOSUserBundle/commit/a9a08c2daf3db38697a8bd4b6e00f42c9a33dd79#diff-36e2e6fca6f6ce7118933033f9ce8bff
I am unit testing a model class and I would like all Doctrine queries to be logged.
My settings.yml for the test environment contains
logging_enabled: true
and my script
$configuration = ProjectConfiguration::getApplicationConfiguration( 'frontend', 'test', true);
new sfDatabaseManager( $configuration ) ;
Still, I don't see any log in any log file.
So, I found a workaround by using an event listener on the Doctrine profiler.
$profiler = new Doctrine_Connection_Profiler();
$conn = Doctrine_Manager::connection();
$conn->setListener($profiler);
/* tests go here */
foreach ($profiler as $event) {
echo $event->getQuery() . "\n";
}
But the same query is printed out several times for some reason (I am sure it is executed only once). Plus it is not so convenient to have the query logs dissociated from the rest of the log messages.