Pergunta

Para fins de teste unitário, preciso simular uma resposta de rede.A resposta normalmente é um fluxo de bytes, armazenado como um const vector<uint8_t>.No entanto, para o teste de unidade, gostaria de produzir o vetor com dados codificados no arquivo CPP ou lidos de um arquivo na mesma solução.Meus dados de exemplo têm cerca de 6 kb.Qual é a orientação geral sobre onde colocar os dados ao usar googleteste?

Foi útil?

Solução

Talvez (a) você precise de uma grande sequência de dados para algum papel em que os casos de teste apenas o lêem.Podem também ser dados globais (de classe), comconst acesso.

Talvez (b) você precise de uma grande sequência de dados para algum papel em que os casos de teste a leitura e modifiquem ou destruam.Isso precisa ser reninitializado por caso de teste e não terconst acesso.

Talvez ambos.Em ambos os casos, uma implementação convencional do GoogleTest usaria um dispositivo de testePara encapsular a aquisição dos dados, adquiriria -os na implementação do jogo virtual do acessório Setup() Função do membro e acesse -a através de um método getter do acessório.

O programa a seguir ilustra um acessório que fornece dados mutáveis ​​por caixa e dados constantes globais adquiridos a partir de arquivos.

#include <vector>
#include <fstream>
#include <stdexcept>
#include "gtest/gtest.h"

class foo_test : public ::testing::Test
{
protected:
    virtual void SetUp() {
        std::ifstream in("path/to/case_data");
        if (!in) {
            throw std::runtime_error("Could not open \"path/to/case_data\" for input");
        }
        _case_data.assign(
            std::istream_iterator<char>(in),std::istream_iterator<char>());
        if (_global_data.empty()) {
            std::ifstream in("path/to/global_data");
            if (!in) {
                throw std::runtime_error(
                    "Could not open \"path/to/global_data\" for input");
            }
            _global_data.assign(
                std::istream_iterator<char>(in),std::istream_iterator<char>());
        }
    }
    // virtual void TearDown() {}   
    std::vector<char> & case_data() {
        return _case_data;
    }
    static std::vector<char> const & global_data() {
        return _global_data;
    }

private:
    std::vector<char> _case_data;
    static std::vector<char> _global_data;

};

std::vector<char> foo_test::_global_data;

TEST_F(foo_test, CaseDataWipe) {
  EXPECT_GT(case_data().size(),0);
  case_data().resize(0);
  EXPECT_EQ(case_data().size(),0);
}

TEST_F(foo_test, CaseDataTrunc) {
  EXPECT_GT(case_data().size(),0);
  case_data().resize(1);
  EXPECT_EQ(case_data().size(),1);
}

TEST_F(foo_test, HaveGlobalData) {
  EXPECT_GT(global_data().size(),0);
}


int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Para o caso (a), você também pode considerar adquirir os dados em um configuração globalfunção de membro por subclasse ::testing::Environment, mas não vejo razão geral para preferir fazê -lo dessa maneira.

...Ou codificá-lo?

Então, para a questão de manter os dados de teste em um arquivo, ou codificar -os na fonte de teste. Os leitores que estão felizes neste momento só ficarão entediados de agora em diante.

Como uma questão geral, isso é uma questão de julgamento no circuito e não acho que o uso do GoogleTest dicas as escalas materialmente.Eu acho que a principal consideração é: É desejável poder variar um item de dados de teste sem reconstruir a suíte de teste?

Digamos que a reconstrução do conjunto de testes para variar este item é um custo não negligenciável e você prevê que o conteúdo do item variará no futuro independentemente do código de teste associado.Ou pode variar, independentemente do código de teste associado, para diferentes configurações do sistema em teste.Nesse caso, é melhor obter o item de um arquivo ou outra fonte que pode ser selecionada pelos parâmetros de tempo de execução do conjunto de testes.No googletest, subclasse class ::testing::Environment é uma instalação projetada para aquisição parametrizada de recursos de suíte de teste.

Se na realidade O conteúdo de um item de dados de teste é vagamente acoplado ao código de teste associado e, em seguida, é improvável que codificá-lo em casos de teste seja uma opção prudente.(E arquivos de teste, em oposição a outros tipos de configurações de tempo de execução, têm a propriedade valiosa de que eles podem ser controlados na versão no mesmo sistema que o código de origem.)

Se o conteúdo de um item de dados de teste é firmemente acoplado ao código de teste associado, sou tendencioso para codificá-lo, em vez de extraí-lo de um arquivo de dados.Apenas tendencioso, não comprometido dogmaticamente.Talvez o seu conjunto de testes empregue instalações robustas de biblioteca para inicializar os dados de teste de API públicos de arquivos XML que também são conectados a sistemas de gerenciamento de testes e gerenciamento de defeitos.Multar!

Eu considero claramente desejável que, se um arquivo de dados de teste for um recurso de teste primário - um que o conjunto de testes não puder gerar -, seu conteúdo tinha melhor dados textuais que um mantenedor competente pode entender e manipular prontamente.Nesse cenário, eu obviamente consideraria que uma lista de constantes hexáticas C/C ++, por exemplo, é dados textuais - É código -fonte.Se um arquivo de teste contiver dados binários ou orientados para a máquina, o conjunto de testes contém melhor os meios de sua produção a partir de recursos primários legíveis.Às vezes, é inevitável que um conjunto de testes dependa de binários "arquetípicos" de origem externa, mas eles quase inevitavelmente envolvem o espetáculo sombrio dos engenheiros de teste e dos bugs que ficam cinza na frente dos editores hexadecimais.

Dado o princípio de que os dados primários do teste devem ser legíveis para os mantenedores, podemos considerá -lo como uma norma de que os dados primários do teste serão "algum tipo de código":Será livre de lógica, mas será o tipo de material textual que os programadores estão acostumados a pesquisar e editar.

Imagine que uma sequência específica de números inteiros não assinados 4096 de 64 bits (a grande tabela mágica) é necessária para testar seu software e está bem casado com o código de teste associado.Pode ser codificado como um enorme vetor ou lista de inicializador de matriz em algum arquivo de origem do conjunto de testes.Pode ser extraído pelo conjunto de testes de um arquivo de dados mantido no formato CSV ou em linhas pontuadas do CSV.

Para extração de um arquivo de dados e contra codificação, pode ser instado (conforme a resposta de Andrew McDonell) que isso valuvelmente alcança uma desajustada de revisões para o BMT das revisões de outro código no mesmo arquivo de origem.Da mesma forma, pode -se pedir que qualquer código -fonte que enquadre as enormes inicializações literais tende a ser inocente e, portanto, um passivo de manutenção.

Mas ambos os pontos podem ser combatidos com a observação de que a declaração definidora do BMT pode ser codificada em um arquivo de origem próprio.Pode ser uma política de revisão de código para o conjunto de testes que as inicializações de dados de testedeve Seja tão codificado - e talvez em arquivos que aderem a uma convenção de nomeação distinta.Uma política fanática, com certeza, mas não mais fanática do que uma que insistiria que todos os inicializadores de dados de teste devem ser extraídos dos arquivos.Se um mantenedor for obrigado a pesquisar o BMT em qualquer arquivo que o contive, não fará diferença se a extensão do arquivo é .cpp, .dat como queiras:tudo o que importa é a compreensibilidade do "código".

Para codificação e contra a extração de um arquivo de dados, pode -se pedir que a extração de um arquivo de dados deve introduzir uma fonte de falhas potenciais irrelevantes em casos de teste - todos os Não deveria acontecer erros que podem derrotar a leitura dos dados corretos de um arquivo.Isso impõe uma sobrecarga no desenvolvimento do teste para efetuar uma distinção correta e clara entre falhas de teste genuínas e falhas na adquirir os dados do teste do arquivo e diagnosticar claramente todas as causas possíveis deste último.

No caso das estruturas GoogleTest e comparativamente funcionais, esse ponto pode ser combatido, até certo ponto, anúncios para as classes base de acessórios polimórficos, como ::testing::Test e ::testing::Environment.Eles facilitam o desenvolvedor de teste na encapsulação da aquisição de recursos de teste na inicialização do teste de teste ou do teste de teste, para que tudo acabasse, seja com sucesso ou com uma falha diagnosticada, antes que os testes constituintes de qualquer caso de teste sejam executados.O RAII pode manter uma divisão sem problemas entre falhas de configuração e falhas reais.

No entanto, há uma sobrecarga irredutível para manuseio de arquivos para a rota do arquivo de dados e Há uma sobrecarga operacional que as características da RAII da estrutura não fazem nada para reduzir.Em minhas negociações com pesados ​​sistemas de teste que negociam em arquivos de dados, os arquivos de dados apenas são Mais propenso a contratempos operacionais do que os arquivos de origem que só precisam estar presentes e corretos no tempo de construção.Os arquivos de dados têm maior probabilidade de aparecer ausentes ou extraviados durante o tempo de execução, ou contendo coisas malformadas, ou de alguma forma, se tornaram negado a permissão ou de alguma forma aparecer na revisão errada.Seus usos em um sistema de teste não são tão simples ou rigidamente controlados quanto os dos arquivos de origem. Coisas que não devem acontecer Acontecer com os arquivos de dados de teste faz parte do atrito operacional de sistemas de teste que se baseiam neles e é proporcional ao seu número.

Desde Arquivos Fonte pode encapsular as inicializações de dados de teste higienicamente para rastreamento de revisão, o codificá-las duro pode ser equiparado à extração de um arquivo, com o pré-processador fazendo a extração como subproduto da compilação.Diante disso, por que empregar outras máquinas, com responsabilidades adicionais, para extraí-lo?Pode haver boas respostas, como a interface XML sugerida com gerenciamento de testes, sistemas de gerenciamento de defeitos, mas "são dados de teste, portanto, não codificá-lo" não é bom.

Mesmo que um conjunto de testes deva suportar várias configurações do sistema em teste que exigem várias instanciações de um item de dados de teste, se o item de dados for convincente com construir configurações do conjunto de testes, você também pode (higienicamente) codificá-lo e deixar a compilação condicional selecionar o codificador correto.

Até agora, não desafiei o argumento de rastreamento de revisão para a segregação baseada em arquivos dos inicializadores de dados de teste.Acabei de enfatizar que arquivos de origem regulares nos quais os inicializadores são codificados podem realizar essa segregação.E não quero derrubar esse argumento, mas quero impedi -lo da conclusão fanática de que os inicializadores de dados de teste devem, em princípio, sempre ser extraído de arquivos dedicados - sejam arquivos de origem ou arquivos de dados.

Não há necessidade de insistir nas razões para resistir a esta conclusão.Dessa forma, está o código de teste que é localmente menos Inteligível do que o programador comum de comer pizza escreveria e organizações de arquivos de suíte de teste que cultivam muito mais rapidamente do que o necessário ou saudável.Normativamente, todos os recursos principais do conjunto de testes são "algum tipo de código".O conjunto de habilidades de um programador inclui a habilidade de particionar o código em arquivos com granularidade apropriada para garantir o hygeine de rastreamento de revisão apropriado.Não é um procedimento mecânico, é uma experiência a ser coberta pela revisão de código.A revisão do código pode e deve garantir que as inicializações de dados de teste, no entanto, sejam realizadas, sejam bem projetadas e criadas em relação à rastreamento de revisão, assim como em todos os outros aspectos rotineiros.

Conclusão:Se você deseja executar a mesma construção do seu conjunto de testes para uma variedade dessas respostas simuladas de rede, leia -o de um arquivo.Se, por outro lado, é invariante ou covariante com configurações de construção do conjunto de testes, por que não Código Hard It?

Outras dicas

(advertência - esta resposta é genérica para qualquer estrutura de teste de unidade)

Eu prefiro manter os arquivos de dados de teste como objetos separados em um sistema de controle de revisão.Isso fornece os seguintes benefícios:

  • Você poderia codificar o teste da unidade para aceitar qualquer ou vários arquivos de dados para testar uma variedade de situações
  • Você pode rastrear alterações nos dados conforme necessário

Se você não quiser a execução de testes da unidade ler um arquivo de dados, que pode ser uma condição necessária em algumas situações, você pode optar por escrever um programa ou script que gera código C ++ que inicializa o vetor na Configuração do Fixture. .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top