Unit testing [PHPUnit],[Symfony]:测试该实体是否保存在数据库中

Unit testing [PHPUnit],[Symfony]:测试该实体是否保存在数据库中,unit-testing,symfony,mocking,phpunit,symfony4,Unit Testing,Symfony,Mocking,Phpunit,Symfony4,我的考试有问题。我学习如何编写phpunit测试,以及如何模拟对象、服务等。。我的ProductService上有以下方法: <?php namespace App\Service; use App\Entity\Product; use App\Repository\ProductRepository; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\O

我的考试有问题。我学习如何编写phpunit测试,以及如何模拟对象、服务等。。我的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();
        }
    }
}
我的产品实体

<?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;
    }
}

首先,在这个测试中,当你模拟一个方法时,原来的方法已经不存在了。在您的例子中,您可以用以下内容替换
ProductService::createProduct

// This is your mock
class ProductService 
{
    // ...

    public function createProduct($productData)
    {
        return $this;
    }
}
你的测试没有检查任何东西

如果您想测试真正的功能,那么

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);
    }
}

您好,谢谢您的回答:)我检查了您的代码,第26行有错误,它是$productRepository,错误:错误:调用上的成员函数get()null@PawelC实际上,您也可以模拟存储库
productRepository=$this->createMock(productRepository::class)
;但是您将无法执行最后一次断言更改这一行,现在我对第28行有问题,它是$entity manager,当我将这一行更改为
$this->createMock(EntityManager::class)
i have error
PHP致命错误:类Mock_ValidatorInterface_b607e347包含6个抽象方法,因此必须声明为抽象的
现在我又犯了一个错误,请查看我的第一篇帖子。我想这不是我的问题phpunit@PawelC检查产品实体注释中是否存在以下行:
@ORM\entity(repositoryClass=“App\Repository\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:
    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
// This is your mock
class ProductService 
{
    // ...

    public function createProduct($productData)
    {
        return $this;
    }
}
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);
    }
}