Wiem, że jest to dyskusyjna praktyka, ale załóżmy, że jest to dla mnie najlepsza opcja. Zastanawiam się, jaka jest właściwa technika, aby to zrobić. Podejście, które widzę jest następujące:
1) Zrób klasę przyjaciela klasą, której metodę chcę przetestować.
2) W klasie przyjaciela utwórz metodę publiczną, która wywołuje metodę prywatną testowanej klasy.
3) Przetestuj publiczne metody klasy przyjaciela.
Oto prosty przykład ilustrujący powyższe kroki:
#include <iostream>
class MyClass
{
friend class MyFriend; // Step 1
private:
int plus_two(int a)
{
return a + 2;
}
};
class MyFriend
{
public:
MyFriend(MyClass *mc_ptr_1)
{
MyClass *mc_ptr = mc_ptr_1;
}
int plus_two(int a) // Step 2
{
return mc_ptr->plus_two(a);
}
private:
MyClass *mc_ptr;
};
int main()
{
MyClass mc;
MyFriend mf(&mc);
if (mf.plus_two(3) == 5) // Step 3
{
std::cout << "Passed" << std::endl;
}
else
{
std::cout << "Failed " << std::endl;
}
return 0;
}
Edytować:
Widzę, że w dyskusji po jednej z odpowiedzi ludzie zastanawiają się nad moją bazą kodu.
Moja klasa ma metody wywoływane przez inne metody; żadna z tych metod nie powinna być wywoływana poza klasą, więc powinny być prywatne. Oczywiście można je zastosować w jednej metodzie, ale logicznie są one znacznie lepiej oddzielne. Metody te są na tyle skomplikowane, że wymagają testów jednostkowych, a ze względu na problemy z wydajnością najprawdopodobniej będę musiał ponownie rozważyć te metody, dlatego byłoby miło mieć test, aby upewnić się, że moje przefakturowanie niczego nie zepsuło. Nie tylko ja pracuję w zespole, ale tylko ja pracuję nad tym projektem, w tym nad testami.
Powiedziawszy powyższe, moje pytanie nie dotyczyło tego, czy dobrą praktyką jest pisanie testów jednostkowych dla metod prywatnych, choć doceniam informację zwrotną.
źródło
Odpowiedzi:
Alternatywą dla znajomego (cóż, w pewnym sensie), którego często używam, jest wzorzec, który poznałem jako access_by. To całkiem proste:
Załóżmy teraz, że klasa B bierze udział w testowaniu A. Możesz to napisać:
Następnie możesz użyć tej specjalizacji access_by, aby wywołać prywatne metody A. Zasadniczo to powoduje obciążenie deklaracją przyjaźni w pliku nagłówkowym klasy, która chce wywołać prywatne metody A. Pozwala także dodawać znajomych do A bez zmiany źródła A. Idiomatycznie wskazuje również każdemu, kto czyta źródło A, że A nie oznacza B prawdziwego przyjaciela w sensie rozszerzenia jego interfejsu. Interfejs A jest raczej kompletny, jak podano, i B potrzebuje specjalnego dostępu do A (testowanie jest dobrym przykładem, użyłem tego wzorca również podczas implementacji powiązań Python Boost, czasem funkcja, która musi być prywatna w C ++ jest przydatna do eksponować w warstwie pytona do implementacji).
źródło
friend access_by
wystarczający, aby pierwszy nie-przyjaciel był wystarczający - będąc zagnieżdżoną strukturą miałby dostęp do wszystkiego w obrębie A? na przykład. coliru.stacked-crooked.com/a/663dd17ed2acd7a3Jeśli trudno go przetestować, jest źle napisany
Jeśli masz klasę z prywatnymi metodami wystarczająco złożonymi, aby uzasadnić własny test, klasa robi za dużo. Wewnątrz jest kolejna klasa próbująca się wydostać.
Wyodrębnij prywatne metody, które chcesz przetestować, do nowej klasy (lub klas) i upublicznij je. Przetestuj nowe klasy.
Oprócz ułatwienia testowania kodu, refaktoryzacja sprawi, że kod będzie łatwiejszy do zrozumienia i utrzymania.
źródło
Nie powinieneś testować prywatnych metod. Kropka. Klasy korzystające z twojej klasy dbają tylko o metody, które udostępnia, a nie te, które używa pod maską do pracy.
Jeśli martwisz się o swój zasięg kodu, musisz znaleźć konfiguracje, które pozwalają przetestować tę prywatną metodę z jednego z publicznych wywołań metod. Jeśli nie możesz tego zrobić, jaki jest sens posiadania tej metody? To po prostu nieosiągalny kod.
źródło
Można to zrobić na kilka sposobów, ale należy pamiętać, że (w istocie) modyfikują one publiczny interfejs twoich modułów, aby dać ci dostęp do wewnętrznych szczegółów implementacji (skutecznie przekształcając testy jednostkowe w ściśle powiązane zależności klienta, gdzie powinieneś mieć żadnych zależności).
możesz dodać deklarację znajomego (klasy lub funkcji) do testowanej klasy.
możesz dodać
#define private public
na początku swoich plików testowych przed#include
sprawdzeniem poprawnego kodu. W przypadku, gdy testowany kod jest już skompilowaną biblioteką, może to spowodować, że nagłówki nie będą już zgodne z już skompilowanym kodem binarnym (i spowodują UB).możesz wstawić makro do testowanej klasy i w późniejszym terminie zdecydować, co to makro oznacza (z inną definicją kodu testowego). Umożliwiłoby to przetestowanie wewnętrznych elementów, ale pozwoliłoby również włamać się do twojej klasy kodu klienta zewnętrznego (tworząc własną definicję w dodawanej deklaracji).
źródło
Oto wątpliwa sugestia na wątpliwe pytanie. Nie podoba mi się łączenie przyjaciela, ponieważ wtedy wypuszczony kod musi wiedzieć o teście. Odpowiedź Nira jest jednym ze sposobów na złagodzenie tego, ale nadal nie lubię zmieniać klasy w celu dostosowania się do testu.
Ponieważ często nie polegam na dziedziczeniu, czasami po prostu zabezpieczam metody prywatne, które w przeciwnym razie są chronione, i dziedziczę klasę testową i udostępniam ją w razie potrzeby. Rzeczywistość jest taka, że publiczny interfejs API i testowy interfejs API mogą się różnić i nadal różnić się od prywatnego interfejsu API, co pozostawia cię w rodzaju powiązania.
Oto praktyczny przykład, dla którego uciekam się do tej sztuczki. Piszę kod osadzony i bardzo polegamy na automatach stanowych. Zewnętrzny interfejs API niekoniecznie musi wiedzieć o stanie wewnętrznego automatu stanów, ale test powinien (prawdopodobnie) przetestować zgodność ze schematem automatu stanów w dokumencie projektowym. Mogę ujawnić moduł pobierający „bieżący stan” jako chroniony, a następnie dać do niego dostęp testowy, co pozwoli mi na pełniejsze przetestowanie automatu stanów. Często uważam, że tego typu zajęcia są trudne do przetestowania jako czarna skrzynka.
źródło
Możesz napisać kod z dużą ilością obejść, aby uniknąć konieczności korzystania ze znajomych.
Możesz pisać klasy i nigdy nie mieć żadnych prywatnych metod. Wszystko, co musisz wtedy zrobić, to wykonać funkcje implementacyjne w jednostce kompilacyjnej, pozwolić klasie zadzwonić do nich i przekazać dowolne elementy danych, do których mają dostęp.
Tak, oznacza to, że możesz zmienić podpisy lub dodać nowe metody „implementacji” bez zmiany nagłówka w przyszłości.
Musisz jednak zastanowić się, czy warto. I wiele będzie naprawdę zależeć od tego, kto zobaczy twój nagłówek.
Jeśli korzystam z biblioteki innej firmy, wolałbym nie widzieć deklaracji znajomych do testerów jednostek. Nie chcę też budować ich biblioteki i uruchamiać ich testów. Niestety, zbyt wiele zewnętrznych bibliotek open source, które zbudowałem, robi to.
Testowanie jest zadaniem autorów biblioteki, a nie jej użytkowników.
Jednak nie wszystkie klasy są widoczne dla użytkownika twojej biblioteki. Wiele klas to „implementacja”, a Ty wdrażasz je w najlepszy sposób, aby zapewnić ich prawidłowe działanie. W tych nadal możesz mieć prywatne metody i członków, ale chcesz, aby testerzy jednostek je przetestowali. Więc idź dalej i zrób to w ten sposób, jeśli dzięki temu szybciej powstanie solidny kod, który jest łatwy w utrzymaniu dla tych, którzy tego potrzebują.
Jeśli wszyscy członkowie Twojej klasy należą do Twojej firmy lub zespołu, możesz także nieco bardziej zrelaksować się na temat tej strategii, zakładając, że zezwalają na to standardy kodowania obowiązujące w Twojej firmie.
źródło