2011-03-31 13 views
17

Opracowujemy aplikację w języku C++ przy użyciu programu Visual Studio 2008 i testów jednostkowych przy użyciu metody Boost.Test. W tej chwili mamy oddzielne rozwiązanie, które zawiera nasze testy jednostkowe.Testowanie jednostek nie wyeksportowanych klas w bibliotece DLL

Wiele naszych projektów w podstawowym rozwiązaniu tworzy biblioteki DLL. Mamy ograniczone pokrycie testowe, ponieważ nie możemy przetestować nieeksportowanych klas.

Mam dwa pomysły na te mogą być testowane:

  1. Export wszystko
  2. umieścić testy wewnątrz DLL (sam projekt i roztwór) i użyć zewnętrznego biegacza Boost.Test za

Nie jestem do końca pewien, jakie byłyby wady. Powyższy numer 1 przerywa enkapsulację na poziomie modułu, a numer 2 może spowodować znacznie większą bibliotekę DLL, chyba że możliwe jest uwzględnienie kodu testowego tylko w niektórych konfiguracjach.

Czy są zatem jakieś poważne wady powyższych metod lub czy można wymyślić inne rozwiązania?

+3

Chciałbym wskazać na [CMake] (http://www.cmake.org) oferującą funkcję o nazwie "biblioteki obiektów". ('add_library (foo_obj OBJECT ...)') W moich projektach buduję źródła w bibliotekach obiektów, które następnie łączę w * obie * bibliotekę DLL ('add_library (foo SHARED ... $ )') * i * jego sterowniki testowe ('add_executable (foo_test ... $ )'). Jest to wariant poniższych odpowiedzi przy użyciu innego systemu kompilacji (dlatego dodałem to jako komentarz, a nie odpowiedź), ale rozwiązuje ten sam problem. – DevSolar

Odpowiedz

9

Rozwijając na odpowiedź Toma Quarendon do this question, Użyłem nieznaczne wariant odpowiedzi Simon Steele:

  • utworzyć projekt testowy (używając niezależnie testową ram chcesz, używam CppUnit).
  • W twoim test_case.cpp, #include <header/in/source/project.h>.
  • We właściwościach projekt testowy:
    • W Linker-> Ogólne, dodać $(IntDir) do dodatkowych katalogów biblioteki projektu źródłowego.
    • W Linker-> Input dodaj pliki .obj do dodatkowych zależności.
  • Dodaj zależność od projektu testowego do projektu źródłowego w Project-> Project Dependencies.

Ponownie, jedyne obciążenie związane z konserwacją jest standardowe w przypadku testów jednostkowych - aby utworzyć zależność od jednostki (jednostek), którą chcemy przetestować.

+2

Kolejny szczegół dotyczący tego podejścia: Jeśli testowana biblioteka DLL używa wstępnie skompilowanych nagłówków, wszystkie pliki .obj, które są połączone w celu utworzenia testowanej biblioteki DLL, zależą od wstępnie skompilowanego pliku nagłówkowego z testowanej biblioteki DLL. Podczas budowania projektu testowego powoduje to błąd łącznika LNK2011: obiekt prekompilowany niepołączony; obraz może nie działać. Oprócz specyficznych plików obiektów, które testujesz, musisz dodać stdafx.obj (jeśli twój plik PCH jest generowany przez kompilację stdafx.cpp). –

+0

Kolejna uwaga na temat tego podejścia: Jeśli projekt testowy korzysta z wielu bibliotek dll zawierających identycznie nazwane pliki obiektów, które wymagają przetestowania, można podać pełną ścieżkę w ustawieniach Linker-> Input, np. '$ (SolutionDir) \ t \ $ (platforma) \ $ (konfiguracja) \ .obj' do rozróżniania między nimi. – Edward

+0

Możesz również potrzebować [/FORCE:MULTIPLE](https://msdn.microsoft.com/en-us/library/70abkas3.aspx), jak właśnie odkryłem. – Rai

3

Rozwiązaniem, którego używam, jest zbudowanie tego samego nieeksportowanego kodu w mojej testowej bibliotece DLL. Zwiększa to czas budowania i oznacza dodanie wszystkiego do obu projektów, ale oszczędza wszystko, eksportując lub umieszczając testy w głównym kodzie produktu.

Inną możliwością jest skompilowanie kodu nie wyeksportowanego do biblioteki używanej zarówno przez bibliotekę DLL z eksportem, jak i projekt testu jednostkowego.

+0

Może to działać w przypadku małych projektów, ale mamy dużo kodu, więc byłoby to koszmarne utrzymanie, aby dokonać zmian w dwóch miejscach. – Jon

+0

Jedyne zmiany, które należy wprowadzić, to dodawanie lub usuwanie plików. Jeśli więc zostanie dodany nowy plik CPP zawierający kod, który musi zostać przetestowany w jednostce, należy go dodać do obu projektów. Nie ma dwóch kopii kodu źródłowego, każdy plik źródłowy zawierający testowalny kod jest właśnie zawarty w obu projektach. –

+0

To było moje pierwsze podejście, które zadziałało dla mnie. Pomyślałem o potencjalnym problemie z tym jednak - czasami projekt dll wykorzystuje inne flagi kompilacji niż projekt testowy.Dlatego też dll i projekty testowe mogą tworzyć różne pliki obiektów dla tego samego pliku źródłowego. Chociaż w moim przypadku byłem prawie pewien, że są one takie same, ogólnie bezpieczniej jest testować pliki obiektów tworzone przez projekt dll, a nie pliki obiektów tworzone przez projekt testowy. Skończyło się na zmianie mojego podejścia do opisanego przeze mnie @Rai. –

0

spróbuj definiowania takich jak następujące gdzieś wszystkie pliki będą obejmować:

#define EXPORTTESTING __declspec(dllexport) 

i używać go w miejsce dllexport, tak:

class EXPORTTESTING Foo 
{ 
... 
}; 

Wtedy będziesz w stanie wyłącz flagę do budowania biblioteki DLL wydania, ale trzymaj ją dla testowalnej biblioteki DLL.

+2

Nie jestem pewien, czy to dobry sposób na zrobienie tego ... Testowalny kod nie powinien być modyfikowany do testowania. Nawet jeśli to proste makro. – toussa

2

Szukałem również rozwiązania, być może poniższe będą łatwiejsze do utrzymania.

Dodaj nową konfigurację kompilacji, np. "Testowanie jednostki Debuguj" do projektu DLL i zmień typ konfiguracji na "Biblioteka statyczna .lib" ("Ogólne" -> "Typ konfiguracji").

Następnie dodaj zależność testów jednostkowych na tym projekcie, teraz wszystko powinno się łączyć, gdy używasz nowej konfiguracji kompilacji "Debugowanie testów jednostkowych". Jeśli korzystasz z wersji rozwojowych do testów jednostkowych, musisz dodać kolejną konfigurację z optymalizacją wersji.

więc korzyści z tego rozwiązania są:

  • niski maintanability kosztować
  • pojedynczy DLL/projektu biblioteka statyczna
  • nie trzeba ręcznie odwołują się do .obj pliki

Wady:

  • Dodatkowa konfiguracja profil (s) racja będzie wymagać pewnych zmian w swoim środowisku kompilacji (CI)
  • większe czasy kompilacji

Aktualizacja: My rzeczywiście skończyło się używając innego podejścia.

Dodaliśmy nową „debug test”/release test”Konfiguracje" dla każdego istniejącego projektu, który mamy.

Dla .exe/.dll projektów możemy wyłączyć oryginalny main.cpp od kompilacji i zastąpić go z jeden, który tworzy instancję testową (np. gtest) i uruchamia wszystkie testy, testy są w oddzielnych plikach .cpp, które są również wyłączone z kompilacji w regularnych konfiguracjach (Release/Debug) i włączane tylko w konfiguracjach testowych

W projektach .lib mamy również nowe konfiguracje "Testuj debugowanie"/"Testuj wersję" i tam konwertujemy bibliotekę statyczną na plik .exe i dostarczamy plik main.cpp, który tworzy strukturę testową nd sam przeprowadza testy i testy. Pliki związane z testami są wykluczone z kompilacji w konfiguracjach Release/Debug.