Anonimowe przestrzenie nazw sprawiają, że kod jest nie do przetestowania

12

Oto typowy kod C ++:

foo.hpp

#pragma once

class Foo {
public:
  void f();
  void g();
  ...
};

foo.cpp

#include "foo.hpp"

namespace {
    const int kUpperX = 111;
    const int kAlternativeX = 222;

    bool match(int x) {
      return x < kUpperX || x == kAlternativeX;
    }
} // namespace

void Foo::f() {
  ...
  if (match(x)) return;
  ...

Wygląda jak porządny idiomatyczny kod C ++ - klasa, funkcja pomocnicza, matchktóra jest używana przez metody Foo, niektóre stałe tej funkcji pomocniczej.

A potem chcę napisać testy.
Byłoby całkowicie logiczne napisanie oddzielnego testu jednostkowego match, ponieważ jest to dość trywialne.
Ale rezyduje w anonimowej przestrzeni nazw.
Oczywiście mogę napisać test, który zadzwoniłby Foo::f(). Jednak nie będzie to dobry test, jeśli Foojest ciężki i skomplikowany, taki test nie izoluje testowanego od innych niepowiązanych czynników.

Więc muszę się wyprowadzić matchi wszystko inne z anonimowej przestrzeni nazw.

Pytanie: po co umieszczać funkcje i stałe w anonimowej przestrzeni nazw, jeśli sprawia, że ​​nie można ich używać w testach?

Abyx
źródło
3
@ BЈовић ponownie przeczytaj kod - dostępna jest anonimowa przestrzeń nazw foo.cpp, a nie nagłówek! OP wydaje się rozumieć całkiem dobrze, że nie należy umieszczać anonowych przestrzeni nazw w nagłówku.
amon
Pomysł w testowaniu jednostkowym kodu C ++ w przestrzeni nazw bez nazwy ... ale „chodzi o to, że enkapsulacja jest dobra. W końcu jest to ten sam problem, jaki masz z prywatnymi funkcjami członka: nie można ich testować w sposób nieinwazyjny, ale nie chcesz rezygnować z ukrywania informacji na potrzeby testów jednostkowych (np. Stackoverflow.com/a/3676680/3235496 ).
manlio
1
Przypadek nie różni się koncepcyjnie od testowania (lub nie testowania) metod prywatnych. Kiedy więc szukasz „Test jednostkowy prywatny” tutaj na Programistach, otrzymasz wiele odpowiedzi, które możesz zastosować bezpośrednio w swojej sprawie.
Doc Brown
@DocBrown Nie pytam, jak testować funkcje w anonie. przestrzenie nazw. Pytam „po co wstawiać kod do anon. Przestrzeni nazw?” (patrz tekst na końcu pytania). Obwiniaj Ixrec za zmianę tytułu na coś innego.
Abyx
2
@Abyx: w tych innych odpowiedziach, o których wspomniałem powyżej, znajdziesz duży konsensus wielu ekspertów, że testowanie metod prywatnych jest naprawdę złym pomysłem i że nadużywanie friendsłowa kluczowego do tego celu nie jest zalecane. Połącz to z założeniem że jeśli ograniczenie metody prowadzi do sytuacji, w której nie można jej więcej bezpośrednio przetestować, oznaczałoby to, że metody prywatne nie byłyby przydatne.
Doc Brown

Odpowiedzi:

7

Jeśli chcesz przetestować jednostkowo prywatne szczegóły implementacji, wykonaj ten sam rodzaj dodge dla nienazwanych przestrzeni nazw jak dla prywatnych (lub chronionych) członków klasy:

Włam się i imprezuj.

Podczas gdy nadużywasz klas friend, dla nienazwanych przestrzeni nazw nadużywasz #includemechanizmu, który nawet nie zmusza cię do zmiany kodu.
Teraz, gdy Twój kod testowy (lub lepiej tylko coś, aby wszystko ujawnić) znajduje się w tej samej JT, nie ma problemu.

Uwaga: jeśli przetestujesz szczegóły implementacji, test zostanie przerwany, jeśli się zmienią. Naprawdę koniecznie przetestuj tylko te szczegóły implementacji, które i tak wycieką, lub zaakceptuj, że Twój test jest niezwykle efemeryczny.

Deduplikator
źródło
6

Funkcja w twoim przykładzie wygląda na dość złożoną i może być lepiej przenieść ją do nagłówka na potrzeby testów jednostkowych.

po co umieszczać funkcje i stałe w anonimowej przestrzeni nazw, jeśli sprawia, że ​​nie można ich używać w testach?

Aby odizolować je od reszty świata. I nie tylko funkcje i stałe można umieszczać w anonimowej przestrzeni nazw - dotyczy to także typów.

Jeśli jednak sprawia, że ​​testy jednostkowe są bardzo złożone, oznacza to, że robisz to źle. W takim przypadku funkcja tam nie należy. Następnie nadszedł czas na trochę refaktoryzacji, aby uprościć testowanie.

Tak więc w anonimowej przestrzeni nazw powinny działać tylko bardzo proste funkcje, czasem stałe i typy (w tym typedefs) używane w tej jednostce tłumaczeniowej.

BЈовић
źródło
5

Byłoby całkowicie logiczne napisanie osobnego testu jednostkowego dla dopasowania, ponieważ jest to dość nietrywialne.

Kod, który pokazałeś, matchto dość trywialny 1-liniowy program bez żadnych trudnych przypadków na krawędziach, czy to jest jak uproszczony przykład? W każdym razie założę, że to uproszczone ...

Pytanie: jaki jest sens umieszczania funkcji i stałych w anonimowej przestrzeni nazw, jeśli sprawia, że ​​nie można ich używać w testach?

To pytanie sprawiło, że wskoczyłem tutaj, ponieważ Deduplicator pokazał już doskonale dobry sposób włamania się i uzyskania dostępu poprzez #includeoszustwo. Ale sformułowanie tutaj sprawia, że ​​brzmi to jak testowanie każdego wewnętrznego szczegółu implementacji wszystkiego, jest to pewnego rodzaju uniwersalny cel końcowy, gdy jest on daleko od niego.

Celem nawet jednostkowych testów nie zawsze jest testowanie każdej drobnej ziarnistej wewnętrznej mikro-jednostki funkcjonalności. To samo pytanie dotyczy funkcji statycznego zakresu plików w C. Możesz nawet trudniej odpowiedzieć na to pytanie, pytając, dlaczego programiści używają pimplsw C ++, co wymagałoby zarówno friendship i #includeoszustwa, jak i białej skrzynki, wymieniając łatwą testowalność szczegółów implementacji w celu poprawy czasów kompilacji, na przykład

Z pewnego pragmatycznego punktu widzenia może to zabrzmieć obrzydliwie, ale matchmoże nie zostać poprawnie zaimplementowane w niektórych przypadkach krawędzi, które powodują potknięcie. Jeśli jednak jedyna klasa zewnętrzna Foo, która ma dostęp, matchnie może jej użyć w sposób, który napotyka te przypadki skrajne, to raczej nie ma znaczenia dla poprawności tego, Fooże matchma przypadki skrajne, które nigdy nie zostaną napotkane, chyba że Foozmiany, w którym to momencie testy Foozakończą się niepowodzeniem i dowiemy się natychmiast.

Bardziej obsesyjny sposób myślenia, chętny do przetestowania każdego szczegółu wewnętrznego wdrożenia (być może np. Oprogramowanie o kluczowym znaczeniu) może chcieć włamać się i imprezować, ale wiele osób niekoniecznie uważa, że ​​to najlepszy pomysł, ponieważ stworzyłoby to najbardziej kruche testy, jakie można sobie wyobrazić. YMMV. Chciałem jednak odnieść się do sformułowania tego pytania, które sprawia, że ​​brzmi to tak, jakby tego rodzaju uber-drobnoziarnista wewnętrzna szczegółowość na poziomie szczegółowości powinna być celem końcowym, kiedy nawet najbardziej rygorystyczny sposób testowania jednostek może się tutaj nieco zrelaksować i unikaj prześwietlania wnętrza każdej klasy.

Dlaczego ludzie definiują funkcje w anonimowych przestrzeniach nazw w C ++ lub jako funkcje statyczne o zasięgu pliku z wewnętrznym powiązaniem w C, ukryte przed światem zewnętrznym? I to głównie o to: aby ukryć je przed światem zewnętrznym. Ma to szereg efektów, od skrócenia czasu kompilacji do zmniejszenia złożoności (to, czego nie można uzyskać gdzie indziej, nie może powodować problemów w innym miejscu) i tak dalej. Prawdopodobnie testowalność prywatnych / wewnętrznych szczegółów implementacyjnych nie jest najważniejszą rzeczą w ludzkich umysłach, kiedy robią to ponad, powiedzmy, skracając czas kompilacji i ukrywając niepotrzebną złożoność przed światem zewnętrznym.


źródło
Chciałbym móc to głosować dziesięć razy. Będąc styczną związaną z oprogramowaniem o kluczowym znaczeniu i o wysokim poziomie bezpieczeństwa, znacznie bardziej zależy Ci na poprawności szczegółów. Ten idiomatyczny styl dla C ++ nie jest do tego odpowiedni. Nie używałbym funkcji tego języka, takich jak anonimowe przestrzenie nazw lub wzorce przypominające proxy. Z grubsza kontrolowałbym swoją granicę za pomocą [obsługiwanych] nagłówków przestrzeni użytkownika i [nieobsługiwanych] nagłówków przestrzeni programisty.
David