Como posso usar objetos simulados nos meus testes de unidade e ainda usar a cobertura do código?

StackOverflow https://stackoverflow.com/questions/336288

Pergunta

Atualmente, estou começando a introduzir o conceito de objetos simulados em meus testes de unidade. Em particular, estou usando a estrutura MOQ. No entanto, uma das coisas que notei é que, de repente, as classes que estou testando usando essa estrutura estão mostrando cobertura de código de 0%.

Agora eu entendo que, como estou apenas zombando da aula, não está executando a própria classe ... mas como faço para escrever esses testes e ter a cobertura de código retorna resultados precisos? Eu tenho que escrever um conjunto de testes que usam zombarias e um conjunto para instanciar diretamente a classe.

Talvez eu esteja fazendo algo errado sem perceber isso?

Aqui está um exemplo de eu tentar testar uma classe chamada "MyClass":

using Moq;
using NUnitFramework;

namespace MyNameSpace
{
    [TestFixture]
    public class MyClassTests
    {

        [Test]
        public void TestGetSomeString()
        {
            const string EXPECTED_STRING = "Some String!";

            Mock<MyClass> myMock = new Mock<MyClass>();
            myMock.Expect(m => m.GetSomeString()).Returns(EXPECTED_STRING);

            string someString = myMock.Object.GetSomeString();

            Assert.AreEqual(EXPECTED_STRING, someString);
            myMock.VerifyAll();

        }

    }

    public class MyClass
    {
        public virtual string GetSomeString()
        {
            return "Hello World!";
        }
    }
}

Alguém sabe o que eu deveria estar fazendo de maneira diferente?

Foi útil?

Solução

Você não está usando seus objetos simulados corretamente. Quando você está usando objetos simulados, você pretende testar como seu código interage com outros objetos sem realmente usar os objetos reais. Veja o código abaixo:

using Moq;
using NUnitFramework;

namespace MyNameSpace
    {
        [TestFixture]
        public class MyClassTests
        {

            [Test]
            public void TestGetSomeString()
            {
                const string EXPECTED_STRING = "Some String!";

                Mock<IDependance> myMock = new Mock<IDependance>();
                myMock.Expect(m => m.GiveMeAString()).Returns("Hello World");

                MyClass myobject = new MyClass();

                string someString = myobject.GetSomeString(myMock.Object);

                Assert.AreEqual(EXPECTED_STRING, someString);
                myMock.VerifyAll();

            }

        }

        public class MyClass
        {

            public virtual string GetSomeString(IDependance objectThatITalkTo)
            {
                return objectThatITalkTo.GiveMeAString();
            }
        }

        public interface IDependance
        {
            string GiveMeAString();
        }
    }

Não parece que está fazendo nada útil quando seu código está apenas retornando uma string sem qualquer lógica por trás dela.

O poder real vem se você GetSomeString() o método fez alguma lógica que pode alterar o resultado da sequência de saída, dependendo do retorno do IDependdance .GiveMeAString() Método, então você pode ver como seu método lida com dados ruins sendo enviados do IDependdance interface.

Algo como:

 public virtual string GetSomeString(IDependance objectThatITalkTo {
     if (objectThatITalkTo.GiveMeAString() == "Hello World")
     return "Hi";
 }

Agora, se você tiver esta linha em seu teste:

myMock.Expect(m => m.GiveMeAString()).Returns(null);

O que acontecerá com o seu GetSomeString() método?

Outras dicas

Grande erro é zombar do Sistema em teste (SUT), você testa outra coisa. Você deve zombar apenas de dependências do SUT.

Eu recomendaria ficar longe de zombar de estruturas até que você entenda as interações que estão acontecendo aqui.

IMO, é melhor aprender com duplas de teste criadas manualmente e depois se formar em uma estrutura zombeteira depois. Meu raciocínio:

  1. Estruturas zombeteiras abstratas do que realmente está acontecendo; É mais fácil entender as interações se você precisar criar suas dependências explicitamente e siga os testes no depurador.

  2. É fácil usar mal as estruturas. Se você rolar o seu próprio quando está aprendendo, é mais provável que você entenda as diferenças entre diferentes tipos de duplas de teste. Se você for direto para uma estrutura de zombaria, é fácil usar zombarias quando você queria stubs e vice -versa - há uma grande diferença.

Pense desta maneira: a classe em teste é o foco. Você cria uma instância, chama seus métodos e afirma que o resultado está correto. Se a classe em teste tiver dependências (por exemplo, algo é necessário no construtor), você satisfaz essas dependências usando as classes A: Real ou B: Duplas de teste.

A razão pela qual usamos duplas de teste é que ele isola a classe em teste, o que significa que você pode exercer seu código de maneira mais controlada.

Por exemplo, se você possui uma classe que contém um objeto de rede, não pode testar as rotinas de manuseio de erros da classe possuir que detectam conexões mortas se você for forçado a usar um objeto de conexão de rede de concreto. Em vez disso, você injeta um objeto de conexão falso e diz para fazer uma exceção quando seu método "SendBytes" é chamado.

Isto é, em cada teste, as dependências da classe em teste são criadas especificamente para exercer uma peça de código específica.

Isso faz muito sentido. Essencialmente, você está dizendo que eu preciso fazer o seguinte:

public class MyClass
{
    public virtual string GetSomeString(MyOtherClass moc)
    {
        return moc.ToString();
    }
}

.....

Mock<MyOtherClass> myMock = new Mock<MyOtherClass>();

MyClass mc = new MyClass();

string someString = mc.GetSomeString(myMock.Object);
Assert.AreEqual(EXPECTED_STRING, someString);

Essencialmente instanciando o SUT e apenas usando zombarias para as classes que o SUT exige?

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