Czy nie należy twierdzić, że nie ma wartości zerowej w instrukcji assert w kodzie produkcyjnym? [Zamknięte]

32

Widziałem to pytanie, ale mam jeszcze kilka pytań dotyczących użycia assertsłowa kluczowego. Rozmawiałem z kilkoma innymi programistami o użyciu assert. W tym przypadku użycia istniała metoda, która może zwrócić wartość null, jeśli zostaną spełnione pewne wymagania wstępne. Kod, który napisałem, wywołuje metodę, a następnie zapewnia, że ​​nie zwraca wartości null, i nadal używa zwróconego obiektu.

Przykład:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

Teraz wyobraź sobie, że używam tego w ten sposób:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

Powiedziano mi, że powinienem usunąć to assert, że nigdy nie powinny być używane w kodzie produkcyjnym, tylko do testowania. Czy to prawda?

Big_Bad_E
źródło
19
Domyślnie asercje są wyłączone. Musisz jawnie włączyć je w środowisku wykonawczym za pośrednictwem -ealub równoważnego.
jarmod
Powiązane pytanie na temat inżynierii oprogramowania : softwareengineering.stackexchange.com/q/137158/187318 (chociaż jest dość stary, więc tam zaakceptowana odpowiedź nadal zaleca bibliotekę zewnętrzną, która nie jest już konieczna od czasu wprowadzenia Objects.requireNonNullJava 8).
Hulk
Ten tytuł pytania jest zbyt szeroki, ale pytanie zawarte w ciele jest znacznie węższe, a na odpowiedzi w pełni odpowiadają funkcje pomocnicze.
smci
Żeby było jasne, pytasz, czy kod klienta powinien implementować niepuste kontrole / potwierdzenia na obiektach. (Różni się to od pytania, czy sam kod biblioteki powinien gwarantować, że obiekty nie mogą mieć wartości NULL, czy twierdzą, że tam).
smci
1
Możliwe duplikaty: kiedy asercje powinny pozostać w kodzie produkcyjnym?
Peter Mortensen

Odpowiedzi:

19

Użyj Objects.requireNonNull(Object)do tego.

Sprawdza, czy określone odwołanie do obiektu nie ma wartości NULL. Ta metoda jest przeznaczona przede wszystkim do sprawdzania poprawności parametrów w metodach i konstruktorach, [...]

W twoim przypadku byłoby to:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

Ta funkcja została stworzona do celów, o których wspomniałeś, tj. Wyraźnie zaznacz, co nie ma wartości zerowej; także w produkcji. Ogromną zaletą jest to, że upewniasz się, że znajdziesz wartości zerowe dokładnie tam, gdzie nie powinny występować. Będziesz miał mniej problemów z debugowaniem problemów spowodowanych zerowymi wartościami, które zostały przekazane gdzieś, gdzie nie powinny być.

Kolejną korzyścią jest dodatkowa elastyczność w zakresie kontroli zerowej w przeciwieństwie do assert. Chociaż assertjest słowem kluczowym do sprawdzania wartości logicznej, Objects.requireNonNull(Object)jest funkcją i dlatego może być osadzony w kodzie o wiele bardziej elastyczny i czytelny. Na przykład:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Należy pamiętać, że Objects.requireNonNull(Object)służy wyłącznie do sprawdzania wartości zerowej w przypadku assertuogólnienia. assertma nieco inne cele w tym względzie, tj. przede wszystkim testowanie. Musi być włączony, aby można go było włączyć do testowania i wyłączyć do produkcji. W każdym razie nie używaj go do kodu produkcyjnego. Może to spowolnić działanie aplikacji dzięki niepotrzebnym i skomplikowanym sprawdzeniom poprawności przeznaczonym do testowania. Użyj go, aby oddzielić kontrole tylko testowe od kontroli przeznaczonych również do produkcji.

Sprawdź oficjalną dokumentację, aby uzyskać szczegółowe informacje na temat assert.

akuzminykh
źródło
14
Kto i kiedy zadeklarowany twierdzi, że jest dziedzictwem? Jakieś linki / referencje? Nie wyobrażam sobie żadnego rozsądnego programisty, który zdefiniowałby „starsze” narzędzie umożliwiające sprawdzanie zerowej powierzchni, które można łatwo włączyć w testach i wyłączyć w produkcji.
Dmitrij Piskłow
Biorąc pod uwagę, że możesz zdecydować w czasie wykonywania, czy assert działa, ale nie możesz określić w czasie wykonywania, czy NPE jest generowany przez requestNonNull, co masz na myśli mówiąc „masz nad nim większą kontrolę niż z assert”?
Pete Kirkham
@DmitryPisklov Masz rację, edytowałem to. To świetne narzędzie do testowania.
akuzminykh
2
@PeteKirkham Masz rację, kontrola była do tego złym słowem. Mówiłem bardziej o elastyczności składni. Ponadto: tutaj można znaleźć powody, dla których kod miałby generować NPE.
akuzminykh
1
„nie używaj go do kodu produkcyjnego. Może to spowolnić działanie aplikacji” Może, ale może być tego warte. Java ma wbudowane kontrole środowiska uruchomieniowego, aby upewnić się, że nigdy nie zostanie przekroczona tablica. Są one włączane nawet w przypadku wdrożeń produkcyjnych, pomimo możliwej utraty wydajności. Nie mamy nawet wyboru. Sprawdzanie czasu wykonywania w kodzie produkcyjnym nie zawsze jest błędem.
Max Barraclough
22

Najważniejszą rzeczą, o której należy pamiętać, jest to, że można je wyłączyć, więc nigdy nie zakładaj, że zostaną wykonane.

Aby zapewnić zgodność wsteczną, JVM domyślnie wyłącza sprawdzanie poprawności asercji. Należy je jawnie włączyć za pomocą argumentu wiersza polecenia -enableassertions lub jego skrótu -ea:

java -ea com.whatever.assertion.Assertion

Dlatego poleganie na nich nie jest dobrą praktyką.

Ponieważ asercje nie są domyślnie włączone, nigdy nie można zakładać, że zostaną wykonane, gdy zostaną użyte w kodzie. Dlatego zawsze powinieneś sprawdzać wartości puste i puste Opcjonalne, unikać używania asercji do sprawdzania danych wejściowych w publicznej metodzie, a zamiast tego używać niezaznaczonego wyjątku ... Zasadniczo wykonuj wszystkie kontrole tak, jakby tego nie było.

jeprubio
źródło
Oczywiście poleganie na nich jest złą praktyką, ale czy ogólnie jest to zła praktyka?
Big_Bad_E
3
@Big_Bad_E Asercje to narzędzie do debugowania, które powoduje, że program zawiedzie tak wcześnie i możliwie głośno. Zadaniem pakietu testowego jest wówczas upewnienie się, że nie ma możliwości niepowodzenia programu pomimo twierdzeń . Chodzi o to, że gdy pakiet testowy (ma 100% pokrycia, prawda?!?) Uruchamia się bez awarii, zapisz, aby usunąć twierdzenia, ponieważ i tak nie mogą się uruchomić. Oczywiście w tym rozumowaniu jest trochę idealizmu, ale taki jest pomysł.
cmaster
11

Na pewno powiedziano ci rażące kłamstwo. Dlatego.

Asercje są domyślnie wyłączone, jeśli po prostu uruchomisz autonomiczny plik Jvm. Gdy są wyłączone, mają zerową powierzchnię, dlatego nie będą miały wpływu na aplikację produkcyjną. Są jednak prawdopodobnie najlepszymi przyjaciółmi przy opracowywaniu i testowaniu kodu, a większość programów do testowania struktur szkieletowych umożliwia asercje (JUnit robi to), więc kod asercji jest wykonywany po uruchomieniu testów jednostkowych, pomagając wcześniej wykryć potencjalne błędy (np. możesz dodać potwierdzenia dla niektórych kontroli granic logiki biznesowej, co pomoże wykryć kod, który używa niewłaściwych wartości).

To powiedziawszy, jak sugeruje inna odpowiedź, właśnie z tego powodu (nie zawsze są one włączone) nie można polegać na zapewnieniach, aby wykonać niektóre istotne kontrole lub (szczególnie!) Utrzymać dowolny stan.

Aby zobaczyć ciekawy przykład użycia asercji, spójrz tutaj - na końcu pliku znajduje się metoda, singleThreadedAccess()która jest wywoływana z instrukcji assert w linii 201 i ma na celu przechwycenie potencjalnego dostępu wielowątkowego w testach.

Dmitrij Piskłow
źródło
4

Inne odpowiedzi już wystarczająco dobrze to pokrywają, ale są też inne opcje.

Na przykład Spring ma metodę statyczną:

org.springframework.util.Assert.notNull(obj)

Istnieją również inne biblioteki z własnymi Assert.something()metodami. Bardzo łatwo jest także napisać własny.

Pamiętaj jednak, jakie wyjątki zgłaszasz, jeśli jest to usługa internetowa. Poprzednia wspomniana metoda, na przykład, wyrzuca IllegalArgumentExceptiondomyślnie, który wiosną zwraca 500.

W przypadku usługi internetowej często nie jest to wewnętrzny błąd serwera i nie powinien to być 500, ale 400, co jest złym żądaniem.

Christopher Schneider
źródło
1
Jeśli metoda „Assert” nie powoduje natychmiastowego zawieszenia procesu, zamiast tego generując jakiś wyjątek, to nie jest to asercja. Chodzi o assertto, aby natychmiast uzyskać awarię z wygenerowanym plikiem podstawowym, abyś mógł zrobić sekcję pośmiertną za pomocą swojego ulubionego debuggera w miejscu, w którym warunek został naruszony.
cmaster
Aserty nie zawieszają natychmiast procesu. Zgłaszają błąd, który może zostać złapany. Nieobsługiwane wyjątki powodują awarię testów oraz błędy. Jeśli chcesz, możesz argumentować semantykę. Jeśli chcesz, napisz niestandardowe stwierdzenie, które zgłasza błąd zamiast wyjątku. Faktem jest, że w przeważającej większości przypadków właściwym działaniem jest zgłoszenie wyjątku, a nie błędu.
Christopher Schneider
Ach, Java naprawdę assertchce rzucić. Ciekawy. Wersja C / C ++ tego nie robi. Natychmiast podnosi sygnał, że a) zabija proces, i b) tworzy zrzut pamięci. Robi to z jakiegoś powodu: debugowanie takiego niepowodzenia nie jest łatwe, ponieważ nadal masz całą informację o stosie wywołań. Zdefiniowanie assertzamiast tego rzucenia wyjątku, który następnie może zostać (nie) celowo zaprogramowany, pokonuje cel, imho.
cmaster
Podobnie jak niektóre (lub wiele) dziwnych rzeczy w C i C ++, niektóre są w Javie. Jednym z nich jest Throwable. Zawsze można je złapać. Czy to dobrze, czy nie, nie wiem. Myślę, że to zależy. Wiele programów Java jest usługami sieciowymi i byłoby niepożądane, aby zawiesił się z prawie dowolnego powodu, więc prawie wszystko jest przechwytywane i rejestrowane. Jedną z wielkich rzeczy jest ślad stosu, który zwykle wystarcza do zdiagnozowania przyczyny wyjątku lub błędu.
Christopher Schneider
3

Używaj stwierdzeń swobodnie, ilekroć robi to, pomaga wychwycić błędy programowe, np. Błędy.

Nie używaj aser, aby przechwycić coś, co może się zdarzyć logicznie, np. Źle sformatowane dane wejściowe. Użyj potwierdzenia tylko wtedy, gdy błędu nie można naprawić.

Nie umieszczaj żadnej logiki produkcyjnej w kodzie, który jest uruchamiany, gdy sprawdzane jest potwierdzenie. Jeśli twoje oprogramowanie jest dobrze napisane, jest to trywialnie prawdziwe, ale jeśli nie, możesz mieć subtelne skutki uboczne i różne ogólne zachowanie z włączonymi i wyłączonymi asercjami.

Jeśli Twoja firma ma „kod testowy” i „kod produkcyjny”, które robią to samo, ale jako różne podstawy kodu (lub różne etapy edycji), wyjdź stamtąd i nigdy nie wracaj. Próba naprawienia tego poziomu niekompetencji jest prawdopodobnie stratą czasu. Jeśli Twoja firma nie umieści żadnych instrukcji aser poza kodem testów, uprzejmie powiedz im, że aserty są wyłączone w kompilacji produkcyjnej, a jeśli nie są, naprawienie tego błędu jest teraz Twoim priorytetem.

Wartość aserts powinna być dokładnie wykorzystywana w logice biznesowej, a nie tylko w pakiecie testowym. Ułatwia to przeprowadzanie wielu testów wysokiego poziomu, które nie muszą jawnie testować wielu rzeczy, aby przejść przez duże fragmenty kodu i wywołać wszystkie te twierdzenia. W kilku moich projektach typowe testy nawet nie potwierdziły niczego, po prostu nakazały wykonanie obliczeń na podstawie określonych danych wejściowych, co spowodowało sprawdzenie setek twierdzeń i znalezienie problemów nawet w małych kawałkach logiki w głębi.

Kafein
źródło
2

Możesz użyć aser w dowolnym momencie. Nadchodzi debata, kiedy wykorzystać. Na przykład w przewodniku :

  • Nie używaj asercji do sprawdzania argumentów w metodach publicznych.
  • Nie używaj asercji do wykonywania pracy wymaganej przez aplikację do poprawnego działania.
Gatusko
źródło
4
Dobre zasady. Ale odpowiedź byłaby lepsza z kilkoma dodanymi przyczynami.
cmaster