Pergunta

Atualmente, estou trabalhando em uma classe que usa outra classe que possui apenas funções estáticas.

Tudo funcionou bem até eu tentar testar minha aula.

Aqui está um exemplo simples de código do problema:

class A {
    static String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B {
    B() {}
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return A::getSomething();
        } else {
            return "Invalid name!";
        }
    }
}

Assumindo que a classe A está funcionando corretamente (e foi testado por seus testes de unidade), gostaria de verificar o corre algo função na classe B.

Minha primeira opção seria criar zombarias para as classes internas (nesta amostra - classe A), mas nesse caso isso não me dará nada a herdar de A, porque possui apenas funções estáticas.

Minha segunda opção é encapsular as chamadas de uma aula em funções privadas dentro de B para que eu possa controlar seus valores de retorno (embora a escolha dessa opção torne o bem um pouco mais complexo).

Minha pergunta para você é: Existem de maneiras melhores para testar as classes C ++ que dependem de classes/funções estáticas do que minhas opções atuais?

Desde já, obrigado,

Tal.

Foi útil?

Solução

Tive sucesso em testes de unidade em situações semelhantes, refatorando as referências às funções estáticas (no meu caso, eram dependências externas) em novas funções privadas e sobrescrevê -las no stub testado, para que eu possa recomendar essa abordagem

Se as funções refatoradas permanecerem privadas, elas não devem afetar bastante a complexidade do design e devem ser pequenas o suficiente para não impactar negativamente a legibilidade do código.

Outras dicas

Se você não está usando uma suíte de teste monolítico, é fácil. Presumo que você tenha classe A em A.CPP e Classe B no B.CPP, e os testes para B estão em B_Test.cpp.

Crie um arquivo chamado a_mock.cpp

class A
{
    static String getSometing() {
        return String("Expected Something");
    }
};

Então, ao compilar seu arquivo b_test, basta vincular -se ao A_MOCK.O em vez de AO.

g++ -Wall B_test.cpp B.cpp A_mock.cpp

Você pode passar um ponteiro para a função para o construtor da classe A. Para testar, pode passar um pouco o ponteiro para uma função simulada onde você pode fazer o que quiser.

Por que a função estática? Eu sugeriria não torná -lo estático.

Você pode criar uma interface para a classe A (em C ++, isso significa uma classe com apenas cabeçalhos de função virtual pura) denominada AInterface. A classe A implementaria (herdaria) AInterface e implementaria essas funções virtuais.

Em seguida, passe um ponteiro para esta interface para o construtor da classe B e armazene -o em uma variável de membro chamada m_a. Em seguida, no seu teste, crie o MockClassa que implementa a AINTERFACE. Passe o MockClassa para o construtor Classe B e defina M_A na entrada.

class AInterface
{
   virtual String getSomething() = 0;
}

class A : public AInterface
{
    String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B 
{
    B(AInterface A) :  { m_A = A; }
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return m_A.getSomething();
        } else {
            return "Invalid name!";
        }
    }
    AInterface m_A;
}

Código de teste

class MockClassA : public AInterface
{
    String getSometing() {
        return String("Whatever I want. This is a test");
    }
}   

void test ()
{
   // "MockClassA" would just be "A" in regular code
   auto instanceOfB = B(MockClassA());

   String returnValue = instanceOfB.runSomething("something");
   :
   :
}

Eu diria: "Cara, algumas pessoas fazem testes de unidade longe demais!"

Basta testar as duas classes como uma única unidade. A classe A é codificada na classe B de qualquer maneira.

Você deve participar da aula por modelo e exportar explicitamente essa instanciação (B<A>) para evitar problemas de vinculador, se não estivesse anteriormente embutido, conforme publicado. Dessa forma, você pode inserir outras classes para fins de teste conforme necessário e é uma boa prática de qualquer maneira. Também estou curioso para saber por que seu exemplo se parece muito com Java - eu tive que lê -lo cerca de cinco vezes antes de determinar que ele realmente foi C ++ como indicado.

template<typename T> class BImpl {
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return T::getSomething();
        } else {
            return "Invalid name!";
        }
    }
};
typedef BImpl<A> B; // Just plugs in to existing code.

Agora você pode substituir uma classe simulada por A, mesmo que não possa herdar dela. De fato, isso também é extensível de outra maneira - CRTP.

class A : public BImpl<A> {
    String getSomething() {
        // Now it's non-static! IT'S A MIRACLE!
    }
}

As maravilhas dos modelos nunca deixam de me surpreender.

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