Modelos de origem de teste de unidade

10

Tenho vários modelos na minha extensão personalizada que servem apenas para o propósito de preencher algumas seleções e / ou multiselecionas no formulário de adição / edição de minhas entidades.
Então, eles são o que o magento chama de "modelos de origem".
Os valores envolvidos são sempre os mesmos e os métodos retornam a mesma coisa.
Como devo testar esses aparelhos? Ou melhor ainda, devo escrever testes de unidade para eles?
Aqui está um exemplo.
A classe a seguir é usada para adicionar / editar formulários para um campo chamado typee para a coluna da grade do mesmo campo.

<?php
namespace Sample\News\Model\Author\Source;

use Magento\Framework\Option\ArrayInterface;

class Type implements ArrayInterface
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;

    /**
     * Get options
     *
     * @return array
     */
    public function toOptionArray()
    {
        $_options = [
            [
                'value' => '',
                'label' => ''
            ],
            [
                'value' => self::COLLABORATOR,
                'label' => __('Collaborator')
            ],
            [
                'value' => self::EMPLOYEE,
                'label' => __('Employee')
            ],
        ];
        return $_options;
    }

    /**
     * get options as key value pair
     *
     * @return array
     */
    public function getOptions()
    {
        $_tmpOptions = $this->toOptionArray();
        $_options = [];
        foreach ($_tmpOptions as $option) {
            $_options[$option['value']] = $option['label'];
        }
        return $_options;
    }
}
Marius
fonte

Respostas:

15

Esta é uma ótima discussão, e eu realmente gosto das respostas do @KAndy e do @fschmengler.
Gostaria de acrescentar algumas idéias adicionais que considero valiosas ao fazer uma pergunta como "Devo testar o X?" ou "Como devo testar o X?".

O que poderia dar errado?

  • Eu poderia fazer um erro de digitação idiota (acontece o tempo todo)
    Isso geralmente não justifica escrever um teste.
  • Copiarei o código de que preciso do núcleo ou de um módulo diferente e depois o ajustarei às minhas necessidades?
    Acho isso realmente uma coisa muito perigosa de fazer, que geralmente deixa erros sutis. Nesse caso, sou a favor de escrever um teste, se não for muito caro. Realizar a configuração dos modelos de origem realmente os tornaria mais IMO arriscados.
  • Pode haver um conflito com um módulo diferente?
    Isso quase se aplica apenas ao código de configuração. Nesse caso, eu gosto de ter um teste de integração que me diga quando isso acontece.
  • O Magento poderia alterar a API em uma versão futura?
    Muito improvável nesse caso, pois seu código depende apenas de uma interface. Mas as classes mais concretas estão envolvidas ou, se meu código estender uma classe principal, isso se tornará um risco potencial.
  • Uma nova versão do PHP pode quebrar meu código. Ou talvez eu queira dar suporte ao PHP 5.6 nos próximos anos.
    Mais uma vez, é altamente improvável aqui, mas em alguns casos eu quero que um teste me avise, devo alterar o código no futuro para usar sintaxe incompatível.

Quanto custa testar o código?

Isso tem dois aspectos:

  • A quantidade de esforço e tempo necessários para escrever um teste
  • A quantidade de esforço e tempo necessários para testar o código que estou prestes a escrever manualmente.

Durante o desenvolvimento de algum trecho de código, eu tenho que executar o código que estou escrevendo com bastante frequência até considerá-lo concluído. Obviamente, isso é muito mais fácil com um teste de unidade.

No seu caso, escrever um teste é muito barato. Não leva muito tempo ou esforço. @KAndy está certo em que todo o código precisa ser mantido, mas, novamente, nem todos os testes precisam ser mantidos.
Esse pode ser um exemplo em que eu escreveria um teste de unidade, apenas para verificar se não cometi um erro estúpido e excluí-lo quando a aula terminar. Se um teste não fornecer valor a longo prazo, acho que excluí-lo faz sentido.

Essa pergunta também é importante em termos de escolha do tipo de teste a ser escrito: unidade ou integração, por exemplo.

Qual o valor do código que estou escrevendo?

Se um pedaço de código que estou escrevendo é essencial para o serviço que um módulo fornece, eu o testo, independentemente de quão trivial seja.
Se é apenas um pequeno método de utilidade, por exemplo, focado na interface do usuário e não faz parte da lógica de negócios, talvez não.

O código precisará mudar?

Com o tempo, me acostumei a ter cobertura de teste, que alterar o código descoberto parece muito inseguro. Isso inclui coisas tão simples como adicionar uma opção a um modelo de origem, mas também mover uma classe para uma pasta / espaço de nome diferente ou renomear um método.
A realização de testes para essas alterações é inestimável.

Precisa de documentação?

Quão difícil é usar o código? No seu exemplo, é trivial, mas em alguns casos mais complexos, fazer um teste é ótimo para fins de documentação para outros desenvolvedores (ou para mim em alguns meses).

Exploração e Aprendizagem

Se estou trabalhando em algum código e não tenho certeza de como testá-lo, acho muito valioso escrever um teste. O processo quase sempre me dá uma compreensão mais profunda do que estou lidando.
Isso é especialmente verdade para desenvolvedores que ainda se consideram aprendendo testes.
Este também é um exemplo em que pode fazer sentido excluir o teste posteriormente, porque o principal valor fornecido foi o aprendizado.

Disciplina e Stress

Aderir ao laço vermelho-verde-refatorar me ajuda a ir rápido. Isto é especialmente verdade sob pressão. Portanto, mesmo que algum pedaço de código não seja realmente digno de teste, eu ainda posso seguir o TDD, especialmente se o código for trivial para teste.
Isso me mantém no fluxo e alerta.

O que e como testar?

Considere também que você pode escrever o teste com granularidade muito diferente.

  • Testando o valor de retorno exato.
    Este será um teste muito rígido que precisará ser ajustado a cada alteração. Deseja que o teste seja interrompido, por exemplo, se a ordem dos itens na matriz de retorno mudar?
  • Testando a estrutura do valor de retorno.
    Para o modelo de origem, isso poderia estar verificando cada sub-matriz como dois registros, um com a labele outro com uma valuechave.
  • Verificando a classe implementa ArrayInterface.
  • Testar a classe fornece getOptions()mesmo que esse método não faça parte da interface implementada.

Para cada coisa possível que possa ser testada, considere valor, manutenção e custo.

Sumário

Para resumir: não há uma resposta única e verdadeira para uma pergunta se algum pedaço de código deve ser testado ou não. A resposta será diferente para cada desenvolvedor, dependendo das circunstâncias.

Vinai
fonte
2
Ótima resposta! Quero destacar a parte "excluir testes quando eles não fornecem mais valor" - às vezes testes antigos que ajudaram durante o desenvolvimento inicial, estão apenas atrapalhando o longo prazo.
Fabian Schmengler 6/06/16
1
você acabou de responder a 2 outras perguntas que eu tinha dúvidas. obrigado
Marius
6

Na minha opinião, não há resposta geral para "escrever testes de unidade para modelos de origem, sim ou não"

Eu escrevi testes de unidade para modelos de origem, mas esses eram modelos de fonte dinâmica que buscavam dados externos e aí faz todo o sentido.

Para modelos de origem que nada mais são do que matrizes glorificadas (como no seu exemplo), eu não me incomodaria em escrever testes de unidade. Mas, de alguma forma, você precisa garantir que não cometeu um erro. Existem várias opções:

  1. teste de unidade
  2. teste de integração
  3. veja manualmente a configuração

Você está seguindo TDD? Depois escolha entre (1) e (2) ou ambos. Caso contrário, escolha entre (2) e (3).


Se você usar os modelos de origem para opções de configuração do sistema, um teste de integração (um teste para todas as suas opções de configuração) poderá renderizar a seção de configuração e verificar se os <select>elementos estão presentes e contêm os valores esperados. Lembre-se de que não se trata de testar uma classe específica, mas de testar se tudo está conectado corretamente.


E como o @KAndy disse, o ideal é que você não precise de tanto clichê, apenas estenda uma classe base que já é testada em unidade e substitua uma propriedade ou defina os valores na configuração externa.

Nesse cenário, os testes de unidade para implementações concretas não fornecem nenhum valor adicional. Se você tem muitas dessas classes, pode ser uma boa ideia escrever uma classe base ArraySource, contanto que o Magento não a forneça.


Com essa classe base, seu modelo de origem pode ficar assim:

class Type extends ArraySource
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;
    protected $elements = [
        self::COLLABORATOR => 'Collaborator',
        self::EMPLOYEE     => 'Employee',
    ];
    protected $withEmpty = true;
    protected $translate = true;
}
Fabian Schmengler
fonte
Obrigado pela confirmação. Tentarei transformar minhas matrizes glorificadas em uma lista de opções configurável, mas o mesmo se aplica aos modelos que terão exatamente N opções? Como um modelo de origem "status".
Marius
Eu não ver como um "status" modelo de fonte seria diferente para o seu exemplo "tipo de autor"
Fabian Schmengler
porque eu posso usar os valores 0 e 1 (como constantes de classe) no frontend, para filtrar, por exemplo, as entidades ativadas. Quero dizer, neste caso, os valores das opções têm lógica por trás deles. eles não são apenas key=>valuepares.
Marius
Mas essa lógica não faz parte do modelo de origem, certo? Isso significa que não importa aqui. Você ainda terá as constantes para usar em outros lugares. Eu adicionei um exemplo de como eu usaria essa implentação.
Fabian Schmengler 06/06/19
5

Como devo testar esses aparelhos?

Eu acho que você não deveria.

Adicione código ao sistema para aumentar o custo de suporte e manutenção, mas o processo de teste deve ser LEAN .

Mais sobre esse código não deveria existir. Eu acredito que no Magento deve haver uma maneira declarativa genérica de definir conjuntos de opções para usar em lugares diferentes. E sua relutância em escrever teste para esta classe me mostra cheiro de código ruim

KAndy
fonte
1
Obrigado. Então você está dizendo que essas opções não devem ser codificadas, mas devem ser de um arquivo de configuração (di.xml, por exemplo). Eu posso fazer isso. Mas o mesmo vale para os modelos de fonte de status? Quero dizer, se eu tiver a mesma classe acima, mas apenas com o status ativado e desativado (1,0), devo usar a mesma abordagem de configuração? Em caso afirmativo, quero dizer que me parece um excesso de engenharia.
Marius