Hinweis: Dies ist Symfony < 2.6, aber ich glaube, die gleiche Gesamt Problem unabhängig von der Version giltWie wird eine Testisolierung mit Symfony-Formularen und Datentransformatoren erreicht?
dieses Formulartyp zu starten, betrachten, die ein-oder-mehr Einheiten zu repräsentieren als verstecktes Feld ausgelegt ist (Namespace Material der Kürze halber weggelassen)
class HiddenEntityType extends AbstractType
{
/**
* @var EntityManager
*/
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['multiple']) {
$builder->addViewTransformer(
new EntitiesToPrimaryKeysTransformer(
$this->em->getRepository($options['class']),
$options['get_pk_callback'],
$options['identifier']
)
);
} else {
$builder->addViewTransformer(
new EntityToPrimaryKeyTransformer(
$this->em->getRepository($options['class']),
$options['get_pk_callback']
)
);
}
}
/**
* See class docblock for description of options
*
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'get_pk_callback' => function($entity) {
return $entity->getId();
},
'multiple' => false,
'identifier' => 'id',
'data_class' => null,
));
$resolver->setRequired(array('class'));
}
public function getName()
{
return 'hidden_entity';
}
/**
* {@inheritdoc}
*/
public function getParent()
{
return 'hidden';
}
}
Dies funktioniert, dann ist es einfach, und zum größten Teil sieht aus wie wie alle Beispiele, die Sie sehen für das Hinzufügen von Datenwandlern zu einem Formulartyp. Bis Sie zum Komponententest kommen. Siehst du das Problem? Die Transformatoren können nicht verspottet werden. "Aber warte!" Sie sagen: "Komponententests für Symfony-Formulare sind Integrationstests, mit denen sichergestellt werden soll, dass die Transformatoren nicht ausfallen. Das sagt sogar in the documentation!"
Dieser Test überprüft, dass keiner Ihrer Datentransformatoren im Formular fehlgeschlagen ist. Die isSynchronized() Methode wird nur auf falsch gesetzt, wenn ein Daten Transformator eine Ausnahme
Ok wirft, so dann man mit der Tatsache leben, nicht die Transformatoren isolieren kann. Keine große Sache?
Betrachten wir nun, was passiert, wenn das Gerät ein Formular zu testen, die ein Feld dieses Typs hat (unter der Annahme, dass HiddenEntityType
hat & im Service-Container markiert definiert)
class SomeOtherFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('field', 'hidden_entity', array(
'class' => 'AppBundle:EntityName',
'multiple' => true,
));
}
/* ... */
}
tritt nun das Problem. Der Komponententest für SomeOtherFormType
muss jetzt getExtensions()
implementieren, damit der Typ hidden_entity
funktioniert. Wie sieht das aus?
protected function getExtensions()
{
$mockEntityManager = $this
->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
/* Expectations go here */
return array(
new PreloadedExtension(
array('hidden_entity' => new HiddenEntityType($mockEntityManager)),
array()
)
);
}
Sehen Sie, wo dieser Kommentar ist in der Mitte? Ja, damit dies richtig funktioniert, müssen alle Mocks und Erwartungen, die in der Unit-Test-Klasse für die HiddenEntityType
vorliegen, nun effektiv dupliziert werden. Ich bin damit nicht einverstanden, also was sind meine Optionen?
Injizieren des Transformators als eine der Optionen
Dies würde sehr einfach sein und einfacher verspotten würde, aber letztendlich schlägt nur die Dose die Straße hinunter. In diesem Szenario würde
new EntityToPrimaryKeyTransformer()
nur von einer Formulartypklasse in eine andere wechseln. Ganz zu schweigen davon, dass ich das Gefühl habe, dass die Formulartypen ihre interne Komplexität vor dem Rest des Systems verbergen sollten. Diese Option bedeutet, dass diese Komplexität über die Grenzen des Formulartyps hinausgeht.Injizieren einen Transformator Fabrik von Sorten in den Formulartyp
Dies ist ein typischer Ansatz „newables“ innerhalb einer Methode zu entfernen, aber ich kann das Gefühl nicht los, dass dies nur getan wird, um zu mache den Code testbar und mache den Code nicht wirklich besser. Aber wenn das gemacht wäre, würde es ungefähr so aussehen:
class HiddenEntityType extends AbstractType { /** * @var DataTransformerFactory */ protected $transformerFactory; public function __construct(DataTransformerFactory $transformerFactory) { $this->transformerFactory = $transformerFactory; } public function buildForm(FormBuilderInterface $builder, array $options) { $builder->addViewTransformer( $this->transformerFactory->createTransfomerForType($this, $options); ); } /* Rest of type unchanged */ }
Das fühlt sich gut an, bis ich überlege, wie die Fabrik tatsächlich aussehen wird. Es wird den Entity Manager zuerst injiziert brauchen. Aber was dann?Wenn ich weiter nach unten schaue, könnte diese vermeintlich generische Fabrik alle möglichen Abhängigkeiten für die Erstellung von Datentransformatoren verschiedener Art benötigen. Das ist eindeutig keine gute langfristige Designentscheidung. Also was dann? Re-Label dies als
EntityManagerAwareDataTransformerFactory
? Es fühlt sich hier unordentlich an. IchStuff ich denke nicht an ...
Gedanken? Erfahrungen? Solide Beratung?
Vielen Dank für die Antwort und noch mehr, danke für die klare die Quelle Ihrer Empfehlung. Es ist immer schön, neue Bücher zur Leseliste hinzuzufügen. –