Reputation: 1226
I have problem with my test. I learn how to write phpunit test and how i can mock object, services etc.. I have this method on my ProductService:
<?php
namespace App\Service;
use App\Entity\Product;
use App\Repository\ProductRepository;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\ORMException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ProductService
{
/**
* @var ProductRepository
*/
private $productRepository;
/**
* @var EntityManager
*/
private $entityManager;
/**
* @var ValidatorInterface
*/
private $validator;
/**
* ProductService constructor.
* @param ProductRepository $productRepository
* @param EntityManagerInterface $entityManager
* @param ValidatorInterface $validator
*/
public function __construct(ProductRepository $productRepository, EntityManagerInterface $entityManager, ValidatorInterface $validator)
{
$this->productRepository = $productRepository;
$this->entityManager = $entityManager;
$this->validator = $validator;
}
/**
* @param $productData
* @return Product|string
*/
public function createProduct($productData)
{
$name = $productData['name'];
$quantity = $productData['quantity'];
$sku = $productData['sku'];
$product = new Product();
$product->setName($name);
$product->setQuantity($quantity);
$product->setProductSerial($sku);
$errors = $this->validator->validate($product);
if (count($errors) > 0) {
$errorsString = (string)$errors;
return $errorsString;
}
try {
$this->entityManager->persist($product);
$this->entityManager->flush();
return $product;
} catch (\Exception $ex) {
return $ex->getMessage();
}
}
}
and i write this test:
<?php
namespace App\Tests\Service;
use App\Entity\Product;
use App\Repository\ProductRepository;
use App\Service\ProductService;
use Doctrine\Common\Persistence\ObjectRepository;
use PHPUnit\Framework\TestCase;
class ProductServiceTest extends TestCase
{
/**
* Create product test
*/
public function testCreateProduct()
{
$product = new Product();
$product->setName('tester');
$product->setQuantity(2);
$product->setProductSerial('Examplecode');
$productService = $this->createMock(ProductService::class);
$productService->method('createProduct')->will($this->returnSelf());
$this->assertSame($productService, $productService->createProduct($product));
}
}
When i run phpunit test, then i always have success but my database is empty. How can I be sure that the test works correctly? What is worth fixing and what is not? I wanted to make the launch of tests result in, for example, adding records to the test database, but I have no idea how to do it and how to properly mock it. I using phpunit + Symfony 4.
I used to write tests, but those that asked the endpoint API, and here I want to test services and repositories without endpoints.
I would like to learn how to test and mock websites, repositories, various classes etc.
When i apply answer then i have:
PHPUnit 7.5.17 by Sebastian Bergmann and contributors.
Testing Project Test Suite
?[31;1mE?[0m 1 / 1 (100%)
Time: 542 ms, Memory: 10.00 MB
There was 1 error:
1) App\Tests\Service\ProductServiceTest::testCreateProduct
Doctrine\Common\Persistence\Mapping\MappingException: The class 'App\Repository\ProductRepository' was not found in the chain configured namespaces App\Entity, Gesdinet\JWTRefreshTokenBundle\Entity
D:\warehouse-management-api\vendor\doctrine\persistence\lib\Doctrine\Common\Persistence\Mapping\MappingException.php:22
D:\warehouse-management-api\vendor\doctrine\persistence\lib\Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain.php:87
D:\warehouse-management-api\vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\ClassMetadataFactory.php:151
D:\warehouse-management-api\vendor\doctrine\persistence\lib\Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory.php:304
D:\warehouse-management-api\vendor\doctrine\orm\lib\Doctrine\ORM\Mapping\ClassMetadataFactory.php:78
D:\warehouse-management-api\vendor\doctrine\persistence\lib\Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory.php:183
D:\warehouse-management-api\vendor\doctrine\orm\lib\Doctrine\ORM\EntityManager.php:283
D:\warehouse-management-api\vendor\doctrine\doctrine-bundle\Repository\ContainerRepositoryFactory.php:44
D:\warehouse-management-api\vendor\doctrine\orm\lib\Doctrine\ORM\EntityManager.php:713
D:\warehouse-management-api\vendor\doctrine\persistence\lib\Doctrine\Common\Persistence\AbstractManagerRegistry.php:215
D:\warehouse-management-api\tests\Service\ProductServiceTest.php:28
?[37;41mERRORS!?[0m
?[37;41mTests: 1?[0m?[37;41m, Assertions: 0?[0m?[37;41m, Errors: 1?[0m?[37;41m.?[0m
My Product entity
<?php
namespace App\Entity;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
*/
class Product
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
* @Assert\NotBlank()
*/
private $name;
/**
* @ORM\Column(type="integer")
* @Assert\NotBlank()
*/
private $quantity;
/**
* @Gedmo\Mapping\Annotation\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $createdAt;
/**
* @Gedmo\Mapping\Annotation\Timestampable(on="update")
* @ORM\Column(type="datetime")
*/
private $updatedAt;
/**
* @ORM\Column(type="string")
* @Assert\NotBlank()
*/
private $product_serial;
public function __construct() {
$this->setCreatedAt(new \DateTime());
$this->setUpdatedAt();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getQuantity(): ?int
{
return $this->quantity;
}
public function setQuantity(int $quantity): self
{
$this->quantity = $quantity;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(): self
{
$this->updatedAt = new \DateTime();
return $this;
}
public function getProductSerial(): ?string
{
return $this->product_serial;
}
public function setProductSerial(string $product_serial): self
{
$this->product_serial = $product_serial;
return $this;
}
}
ProductRepository
<?php
namespace App\Repository;
use App\Entity\Product;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
class ProductRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Product::class);
}
}
doctrine.yaml
doctrine:
dbal:
# configure these for your database server
driver: 'pdo_mysql'
server_version: '5.7'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
url: '%env(resolve:DATABASE_URL)%'
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
Upvotes: 1
Views: 3311
Reputation: 863
First of all, when you mock a method the original method doesn't exist any more, in this test. In your case you substitute ProductService::createProduct
with something like this:
// This is your mock
class ProductService
{
// ...
public function createProduct($productData)
{
return $this;
}
}
Your test doesn't check anything.
If you want to test the real functionality then
namespace App\Tests\Service;
use App\Repository\ProductRepository;
use App\Service\ProductService;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ProductServiceTest extends KernelTestCase
{
/**
* Create product test
*/
public function testCreateProduct(): void
{
// We load the kernel here (and $container)
self::bootKernel();
$productData = [
'name' => 'foo',
'quantity' => 1,
'sku' => 'bar',
];
$productRepository = static::$container->get('doctrine')->getRepository(ProductRepository::class);
$entityManager = static::$container->get('doctrine')->getManager();
// Here we mock the validator.
$validator = $this->getMockBuilder(ValidatorInterface::class)
->disableOriginalConstructor()
->setMethods(['validate'])
->getMock();
$validator->expects($this->once())
->method('validate')
->willReturn(null);
$productService = new ProductService($productRepository, $entityManager, $validator);
$productFromMethod = $productService->createProduct($productData);
// Here is you assertions:
$this->assertSame($productData['name'], $productFromMethod->getName());
$this->assertSame($productData['quantity'], $productFromMethod->getQuantity());
$this->assertSame($productData['sku'], $productFromMethod->getSku());
$productFromDB = $productRepository->findOneBy(['name' => $productData['name']]);
// Here we test that product in DB and returned product are same
$this->assertSame($productFromDB, $productFromMethod);
}
}
Upvotes: 1