Recentemente, enviei uma solicitação pull ao Magento para corrigir uma única instância de um gerenciador de objetos sendo usado diretamente.
No entanto, o teste de unidade travis do Magento falhou com o seguinte erro .
Erro fatal do PHP: chame o método indefinido Mock_BlockFactory_4b440480 :: create () em /home/travis/build/magento/magento2/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php na linha 39
Com base na construção do travis, nem sei dizer qual teste falhou. Consegui obter um erro semelhante (idêntico?) Localmente com um rastreamento de pilha
PHP Fatal error: Call to undefined method Mock_BlockFactory_ec77572c::create() in /Users/alanstorm/Documents/github/astorm/magento2/app/code/Magento/Cms/Controller/Adminhtml/Block/Delete.php on line 39
PHP Stack trace:
PHP 1. {main}() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/phpunit:0
PHP 2. PHPUnit_TextUI_Command::main() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/phpunit:55
PHP 3. PHPUnit_TextUI_Command->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/TextUI/Command.php:132
PHP 4. PHPUnit_TextUI_TestRunner->doRun() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/TextUI/Command.php:179
PHP 5. PHPUnit_Framework_TestSuite->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/TextUI/TestRunner.php:426
PHP 6. PHPUnit_Framework_TestSuite->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestSuite.php:675
PHP 7. PHPUnit_Framework_TestCase->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestSuite.php:675
PHP 8. PHPUnit_Framework_TestResult->run() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestCase.php:753
PHP 9. PHPUnit_Framework_TestCase->runBare() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestResult.php:686
PHP 10. PHPUnit_Framework_TestCase->runTest() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestCase.php:817
PHP 11. ReflectionMethod->invokeArgs() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestCase.php:951
PHP 12. Magento\Cms\Test\Unit\Controller\Adminhtml\Block\DeleteTest->testDeleteAction() /Users/alanstorm/Documents/github/astorm/magento2/vendor/phpunit/phpunit/src/Framework/TestCase.php:951
PHP 13. Magento\Cms\Controller\Adminhtml\Block\Delete->execute() /Users/alanstorm/Documents/github/astorm/magento2/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/DeleteTest.php:151
Consegui restringir a falha local a este teste - mas estou com uma certa perda no que está acontecendo.
Meu palpite é que a estrutura de teste zombou automaticamente de um argumento de DI para mim, mas que a zombaria automática está faltando o create
método. Se for esse o caso, então a minha pergunta real é: Como adiciono uma simulação para uma dependência recém-injetada na estrutura de teste do Magento .
No entanto, eu nunca fui tão fundo no buraco de coelho de teste do Magento, então não tenho certeza do que realmente precisa acontecer aqui. Alguém com experiência em testes do Magento pode me esclarecer?
create
, exatamente como aqui .Respostas:
O
\Magento\Framework\TestFramework\Unit\Helper\ObjectManager
não é capaz de criar automaticamente uma simulação de fábrica.(Em uma nota lateral, eu nunca uso o
\Magento\Framework\TestFramework\Unit\Helper\ObjectManager
, pois tento manter a quantidade de mágica em testes de unidade no mínimo.)As seguintes alterações são necessárias para fazer o teste passar:
Primeiro , crie a simulação para a fábrica de blocos de modelos na configuração:
Ou, mais compacto:
Segundo , adicione o novo mock aos argumentos do construtor dos controladores:
Terceiro , atualize os testes para que não esperem mais que o
Block
modelo seja criado pelo gerenciador de objetos simulados, mas pela nova fábrica:Substituir
com
Depois disso, o teste passa.
Limpeza adicional
Os pontos a seguir não têm nada a ver com sua pergunta, mas não consigo resistir a escrevê-los.
A
$objectManagerMock
propriedade agora está obsoleta e todas as referências a ela podem (na verdade, devem ) ser removidas da classe de teste.A seguir, desde o PHP 5.5, a
::class
constante está disponível. É preferível usar strings para nomes de classes, pois ajuda na refatoração automática no IDE e na localização de usos de uma determinada classe. Isso torna o PHPStorm mais inteligente. Assim, gostaria de substituir todos os nomes de classe string com o constante, por exemplo,'Magento\Framework\App\RequestInterface'
com\Magento\Framework\App\RequestInterface::class
.Além disso, eu questiono o uso de
\Magento\Framework\TestFramework\Unit\Helper\ObjectManager
.Na minha opinião, é melhor instanciar a classe em teste manualmente usando
new
. A única coisa que o ajudante faz nesse momento é criar uma\Magento\Framework\Registry
zombaria. Prefiro criar isso sozinho e especificá-lo como um argumento construtor. Dessa forma, todas as dependências são claras ao ler o código de teste.A próxima limpeza é bastante importante. Eu mudaria os métodos de teste de unidade para não refletir exatamente a implementação.
Por exemplo, considere a configuração da solicitação simulada em
testDeleteActionThrowsException
:Realmente importa quantas vezes
getParam
é chamado? O teste deve falhar se for chamado duas vezes ou não for o caso? Eu acho que isso não é importante, desde que testemos o resultado final do método é o que esperamos.Vincular o código de teste mais de perto à implementação então necessária leva a testes rígidos que são mais difíceis de manter.
Então, neste exemplo, eu refatoraria para
E, finalmente, como
expects($this->any())
é o padrão, é bom removê-lo para reduzir a quantidade de lixo.Isso parece muito melhor.
Pode-se argumentar que pode fazer sentido especificar o parâmetro esperado
getParam
nesse teste, mesmo que o autor do teste original o tenha omitido.Provavelmente é assim que eu deixaria o teste e seguiria em frente.
Mais um pensamento: o problema com os métodos getter
getParam
é que, se um chamador tenta acessar valores diferentes, o mock deve retornar coisas diferentes com base no valor do argumento.Tais mudanças no futuro são bastante prováveis, portanto, às vezes, eu especifico um mapa de valor de retorno, mesmo se houver apenas um valor. Isso facilita a manutenção do teste quando a classe que está sendo testada mudar no futuro.
Caso você não esteja familiarizado com os mapas de valores de retorno PHPUnit, o
null
valor na matriz é o segundo parâmetro opcional paragetParam($key, $defaultValue = null)
.fonte