Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
147 lines (109 sloc) 4.5 KB
date title categories desc
2019-05-06 22:23:40 -0300
Visual Studio Unit Test (C++)
code
Como funciona a interface entre unit tests em C++ e o Visual Studio?

Desde o Visual Studio 2015 há suporte a unit tests em C++ automatizado na IDE. Porém, a partir do VS 2017 15.5 o suporte aumentou drasticamente, vindo embutidos os suportes para as bibliotecas de teste Google Test, Boost.Test e CTest. Além, é claro, do Microsoft Unit Testing Framework for C++, o caseiro da M$.

Além disso, é possível você mesmo integrar o Visual Studio com outra lib de testes. Mas para que gastar tempo? Várias integrações já estão disponíveis no Visual Studio Marketplace. Ligue já!

OK, parei com o merchan. Até porque não ganho nada com isso. Vamos ao código.

Pelo Wizard do VS podemos criar para um projeto C++ qualquer um projeto de teste. No momento estou vendo os tipos de projeto Native Unit Test e Google Test.

Este é nosso projeto de exemplo:

#include "CalculatorTabajara.h"

int soma(int x, int y)
{
	return x + y;
}

int subtrai(int x, int y)
{
	return x - y;
}

int multiplica(int x, int y)
{
	return x * y;
}

int divide(int x, int y)
{
	return x / y;
}

int main()
{
}

Para conseguir testar o projeto principal adicione-o como referência.

Após isso basta incluir algum header que contenha os tipos, funções, classes e métodos que deseja testar e vá criando métodos de teste dentro da classe de exemplo:

#include "pch.h"
#include "CppUnitTest.h"
#include "..\CalculatorTabajara.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTest1
{
	TEST_CLASS(UnitTest1)
	{
	public:
		
		TEST_METHOD(TestaSoma)
		{
			int z = soma(3, 2);
			Assert::AreEqual(z, 5);
		}

		TEST_METHOD(TestaSubtracao)
		{
			int z = subtrai(3, 2);
			Assert::AreEqual(z, 1);
		}

		TEST_METHOD(TestaMultiplicacao)
		{
			int z = multiplica(3, 2);
			Assert::AreEqual(z, 6);
		}

		TEST_METHOD(TestaDivisao)
		{
			int z = divide(3, 2);
			Assert::AreEqual(z, 1);
		}
	};
}

Agora abrindo o jogo para você, amigo programador C++ que gosta de saber tudo que ocorre debaixo dos panos:

  • Um projeto Unit Test é apenas uma DLL com uns códigos de template.
  • Esse código já adiciona a lib de unit test da Microsoft e cria uma classe com exemplo de uso.
  • Adicione todo código do projeto original que ele precisa para compilar.

Por isso eu tirei a tranqueira de precompiled header do projeto de unit test, retirei a referência (sugestão do tutorial da Microsoft) e apenas adicionei o mesmo cpp para ser compilado.

Agora mais mágica: se você abrir a janela Test Explorer ele irá encontrar seus testes e enumerá-los!

Se você já programou um pouco em Windows com C++ já deve saber o truque: como o Unit Test é uma DLL ela simplesmente exporta os símbolos necessários para que o Visual Studio encontre o que precisa. O básico que um plugin dos velhos tempos faz: exportar interfaces com um pouco de reflection.

Se você habilitar Undecorate C++ Functions no Dependency Walker verá que ele exporta justamente uma espécie de reflection, na forma de structs:

E se você prestar atenção na ordem de exportação desse símbolos verá que o primeiro se chama GetTestClassInfo. Acabou a magia, não é mesmo?

Os headers e fontes do CppUnitTest ficam em paths do Visual Studio como VC\Auxiliary\VS\UnitTest, nas pastas include e lib. Nele é possível dar uma olhada no significado das macros e das classes disponibilizadas. Logo abaixo das macros, no arquivo principal, é possível ver como funciona o reflection:

namespace Microsoft{ namespace VisualStudio {namespace CppUnitTestFramework
{

	struct ClassMetadata
	{
		const wchar_t *tag;
		const unsigned char *helpMethodName;
		const unsigned char *helpMethodDecoratedName;
	};

	struct MethodMetadata
	{
		const wchar_t *tag;
		const wchar_t *methodName;
		const unsigned char *helpMethodName;
		const unsigned char *helpMethodDecoratedName;
		const wchar_t *sourceFile;
		int lineNo;
	};

	struct ModuleAttributeMetadata
	{
		enum AttributeType { MODULE_ATTRIBUTE };
		const wchar_t *tag;
		const wchar_t *attributeName;
        //...

É uma lib pequena e elegante que permite uma interação não apenas com a IDE, como poderia ser automatizada por um script, uma vez que sabe-se o funcionamento interno e algumas interfaces.

You can’t perform that action at this time.