如何对含EntityType字段的Symfony 4表单进行单元测试及报错排查
Hey there! Let's figure out how to fix that error when unit testing your Symfony 4 form with an EntityType field. First, let's break down what's usually going wrong here—EntityType relies on Doctrine's EntityManager to fetch entities, so unit tests (which should avoid real database calls) need to mock those dependencies instead of using the real thing.
Step 1: Use Symfony's Form Test Base Class
Symfony provides a TypeTestCase specifically for testing form types. It sets up the form factory and other necessary services for you, so you don't have to build everything from scratch.
Step 2: Mock Doctrine Dependencies
You'll need to mock the EntityManager and EntityRepository to simulate fetching entities without hitting a real database. Here's a complete example test class:
use App\Form\ProductType; use App\Entity\Product; use App\Entity\Category; use Symfony\Component\Form\Test\TypeTestCase; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ObjectManager; use PHPUnit\Framework\MockObject\MockObject; class ProductFormTest extends TypeTestCase { /** @var MockObject|ObjectManager */ private $entityManagerMock; /** @var MockObject|EntityRepository */ private $categoryRepositoryMock; protected function setUp(): void { parent::setUp(); // 1. Mock the Category repository to simulate entity data $this->categoryRepositoryMock = $this->createMock(EntityRepository::class); // Return dummy categories for the form's choice list $this->categoryRepositoryMock->method('findAll') ->willReturn([ (new Category())->setId(1)->setName('Electronics'), (new Category())->setId(2)->setName('Clothing'), ]); // 2. Mock the EntityManager to return our mocked repository $this->entityManagerMock = $this->createMock(ObjectManager::class); $this->entityManagerMock->method('getRepository') ->with(Category::class) ->willReturn($this->categoryRepositoryMock); } public function testSubmitValidData() { // Sample form data (use entity IDs for EntityType fields) $formData = [ 'name' => 'Wireless Headphones', 'category' => 1, // Matches the ID of our mocked Electronics category // Add other form fields as needed for your Product entity ]; // Create the form, passing the mocked EntityManager as an option $form = $this->factory->create(ProductType::class, null, [ 'entity_manager' => $this->entityManagerMock, ]); // Set up the expected Product entity after form submission $expectedProduct = new Product(); $expectedProduct->setName('Wireless Headphones'); // Mock the repository's find method to convert the submitted ID back to an entity $selectedCategory = (new Category())->setId(1)->setName('Electronics'); $this->categoryRepositoryMock->method('find') ->with(1) ->willReturn($selectedCategory); $expectedProduct->setCategory($selectedCategory); // Submit the form data $form->submit($formData); // Verify the form behaves as expected $this->assertTrue($form->isValid()); $this->assertEquals($expectedProduct, $form->getData()); } }
Key Details to Get Right:
- Form Type Configuration: Your
ProductTypeneeds to accept the EntityManager as an option for theEntityTypefield. Here's how that should look in your form class:// In App\Form\ProductType.php public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name', TextType::class) ->add('category', EntityType::class, [ 'class' => Category::class, 'choice_label' => 'name', 'entity_manager' => $options['entity_manager'], ]); } public function configureOptions(OptionsResolver $resolver) { $resolver->setRequired('entity_manager'); $resolver->setAllowedTypes('entity_manager', ObjectManager::class); } - Mock Behavior: We simulate both
findAll()(for the form's choice list) andfind()(to convert the submitted ID back to an entity) to mirror real Doctrine behavior without a database. - Isolation: Unit tests should be fast and self-contained—using mocks ensures you don't need a test database or full Symfony kernel running.
Why You're Likely Seeing That Error
Most often, the error occurs because the form can't access the EntityManager (either it's not injected properly in the form type, or you forgot to pass the mocked instance in the test). This breaks the initialization of the EntityType field, leading to the test failure.
内容的提问来源于stack exchange,提问作者murtho

