Odczytanie rzuconego wyjątku z CMD za pomocą ProcessBuilder.

Odczytanie rzuconego wyjątku z CMD za pomocą ProcessBuilder.
NO
  • Rejestracja:prawie 4 lata
  • Ostatnio:ponad 3 lata
  • Postów:6
0

Cześć! Testuję aplikację konsolową, która pobiera wzór String jako pierwszy parametr oraz kolejny ciąg znaków String jako drugi parametr i sprawdza czy wzór znajduje się w ciągu oraz po którym znaku z ciągu występuje.
Testuję z użyciem JUnit, Cucumber i Gherkin.

Chcę przetestować, że aplikacja rzuci ArrayIndexOutOfBoundsException w momencie gdy pierwszy, drugi lub obydwa parametry są puste. Jak dotąd najbardziej sensowną metodą na to wydaje mi się, otworzenie CMD za pomocą ProcessBuilder, tam podanie ścieżki do aplikacji oraz podanie parametrów. Ta część działa, faktycznie otwiera mi się okno konsoli i widzę, że wyjątek jest rzucony. Jednak chcę to potwierdzić za pomocą asercji, a żeby to zrobić potrzebuję odczytać wyjątek z konsoli z pomocy metody testowej w moim step definition. No i tutaj rodzi się problem, czy coś takiego jest w ogóle możliwe, a jeśli tak to w jaki sposób mogę to zrobić? Wydaje mi się, że powinienem kombinować z getInputStream() / getOutputStream() z klasy Process niestety zupełnie nie mam pomysłu jak to zrobić. Poniżej moje metody @Given i @When ze step definition. Sama asercja wyjątku planowana jest w metodzie @Then.

Kopiuj
@Given("Parameters list is empty.")
    public void parametersListIsEmpty() {
        args = new String[2];
        args[0] = " ";
        args[1] = " ";

        stringBuilder = new StringBuilder();
        stringBuilder.append("\" java PatternSearch.java ");
        stringBuilder.append(args[0]);
        stringBuilder.append(" ");
        stringBuilder.append(args[1]);
        stringBuilder.append("\"");
    }

    @When("App is run with invalid parameters")
    public void appIsRunWithInvalidParameters() {
        String filePath = FileSystems.getDefault().getPath("./"
                + "\\src\\main\\java\\com\\sample\\textsearch\\").toAbsolutePath().toString();
        String command = stringBuilder.toString();
        String[] processCommand = {"cmd.exe", "/c", "start", "cmd.exe", "/k", command};

        System.out.println(filePath);

        File log = null;

        try {
            ProcessBuilder proBuilder = new ProcessBuilder(processCommand);
            proBuilder.directory(new File(filePath));
            proBuilder.inheritIO();

            log = new File("CMDOutput.txt");

            proBuilder.redirectErrorStream(true);
            proBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(log));

            Process process = proBuilder.start();

            //one attempt to see CMD output
            String output = new String(process.getInputStream().readAllBytes());
            System.out.println(output);

            //another attempt to see CMD output
            System.out.println(processOutput(process));

        } catch (IOException e) {
            e.printStackTrace();
            excMessage = e.getMessage();
            System.out.println(excMessage);
        }
    }

    public String processOutput(Process process) {
         String output = null;

        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            output = reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return output;
    }

ProcessBuilder i Process to na razie dla mnie czarna magia. Mój kod tworzy jeden proces cmd.exe i w nim dopiero drugi taki proces wraz ze ścieżką do aplikacji oraz komendą, domyślam się więc, że potrzebuję wyłuskać output z tego drugiego procesu.
Z góry ogromnie dziękuję za jakiekolwiek wskazówki.

AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około miesiąc
  • Postów:3561
0

Aplikacji się (ZTCW) nie testuje. W ogóle trudno mówić o testowaniu czegoś, co być może - nie znamy wnętrza "czarnej skrzynki" jest proceduralnym spagetti (mam na myśli, ze potrafię szanować DOBRE programowanie strukturalne)

testuje się np metody KLASY, która to klasa może stanowić 98% aplikacji, ale to jednak coś innego.
Wystarczy, że będziesz myślał kategoriami "lightweight main", czyli taki mający jedną, góra dwie linijki.

Podobnie, jak nie będziesz testował loadera JAR-a z twoim main'em itd - a od tego, jak od setki innych powodów, zależy przechodzenie wyjątku zabijającego do środowiska


Bo C to najlepszy język, każdy uczeń ci to powie
NO
  • Rejestracja:prawie 4 lata
  • Ostatnio:ponad 3 lata
  • Postów:6
0

@AnyKtokolwiek: Napisałem "aplikacja" ponieważ całość mieści się w jednej klasie. Jeśli dobrze rozumiem, sugerujesz w ogóle nie testować takiego przypadku? Chciałem po prostu przetestować możliwe najwięcej scenariuszy.

Biorąc pod uwagę, że testuję z użyciem Cucumber czyli BDD, postanowiłem testować tę aplikację end-to-end. W ogóle nie testuję pojedynczych metod ponieważ jak rozumiem nie na tym polega BDD i chodzi właśnie o przetestowania zachowania całej aplikacji/klasy. Z tego też względu wydawało mi się, że rzucenie wyjątku (czyli zakończenie działania aplikacji) jest jednym z zachowań, w przypadku gdy nie podamy danych wejściowych.

Nie ukrywam, że dopiero zaczynam się uczyć BDD i o ile składania jest banalna to mam trochę problem z określeniem co powinno zostać przetestowane, a co zostawić dla testów jednostkowych.

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

Proces który odpalasz to cmd.exe i on działa ok. Trzeba by odpalić bezpośrednio to co odpalasz, żeby móc sensownie z tym pracować. Niemniej powinno się dać odczytać stdout stderr z tego procesu i coś wyłuskać z outputu, jeśli ta twoja aplikacja w ogóle wypisuje jakieś komunikaty błędów.

Niemniej może prościej byłoby w teście zwyczajnie zawołać main() z tymi parametrami? ;)


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 1x, ostatnio: Shalom
AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około miesiąc
  • Postów:3561
0
Shalom napisał(a):

Niemniej może prościej byłoby w teście zwyczajnie zawołać main() z tymi parametrami? ;)

Dokładnie to chciałem powiedzieć ... prawie ... bo w moim marzeniu main() jest tak minimalny, że niemal zerowy

testowanie całego runtimu windowsa / pokoleń loaderów javy / antywirusów / ustawienia terminala / h wie czego jeszcze ... to mam mały sens. Coś wykaże na czerwono lub zielono, ale co naprawdę?


Bo C to najlepszy język, każdy uczeń ci to powie
edytowany 1x, ostatnio: AnyKtokolwiek
AK
Pamiętacie wylot części softu koło wersji 6 / 7 gdy string copyright się zmienił z Suna na Oracle'a (ze sporym zresztą opóźnieniem w stosunku do biznesowego czasu rzeczywistego) ? Problem? Na pewno. Czyj błąd ...
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:29 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
2
AnyKtokolwiek napisał(a):

Aplikacji się (ZTCW) nie testuje.

Testuje się. Nie raz stawiałem całą aplikację webową żeby uderzać w testach po prawdziwym http :)
W przypadku aplikacji konsolowych jest to na pewno rzadsze (ale aplikacje konsolowe rzadziej się pisze w Javie) i trudniejsze (interfejs CLI jest różny między systemami, w przeciwieństwie do http) oraz ma mniej sensu (aplicacje konsolowe są zwykle małe i łatwiej je testować po kawałku niż aplikacje webowe).
Kiedyś próbowałem testować tak swoją aplikację konsolową i mało z tego zysku było. Ostatecznie większość testów przepisałem na normalne testy gdzie wywoływałem metodę main, albo moją funkcję run zawierającą interesujący mnie kod biznesowy

nie znamy wnętrza "czarnej skrzynki"

Jakbyś znał wnętrze czarnej skrzynki to nie była by już czarną skrzynką tylko białą skrzynką. Na tym polegają testy czarnoskrzynkowe że nie znasz wnętrza a interesuje cię tylko API :)


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
AK
> próbowałem testować tak swoją aplikację konsolową i mało z tego zysku było. Dokladnie to mam na mysli
NO
  • Rejestracja:prawie 4 lata
  • Ostatnio:ponad 3 lata
  • Postów:6
0

@Shalom: @AnyKtokolwiek: Moje wcześniejsze metody testowe opierają się na wyniku wywołania metody main() jednak to nie zadziała w momencie testowania pustej listy argumentów ponieważ wtedy rzucony zostaje NullPointerException, a to nie to samo co zostaje rzucone gdy odpalę aplikację z konsoli bez podania argumentów. Jeśli w konsoli nie podam argumentów to rzucony jest ArrayIndexOutOfBoundException, stąd pomysł na zrobienie tego przy pomocy ProcessBuildera ale jak rozumiem z powyższych wypowiedzi nie ma to większego sensu.

AK
To niekompatybilnie ze specyfikacją przygotowujesz parametry. Pusty string linii komendy != null dla maina, i tyle. testy dotyczą nie twojego kodu, a kodu rozbiegowego
NO
  • Rejestracja:prawie 4 lata
  • Ostatnio:ponad 3 lata
  • Postów:6
0

@AnyKtokolwiek: niestety nie znam pojęcia "kod rozbiegowy".
Wiem, że pusty String linii komendy != null ale to nadal nie rozwiązuje problemu ponieważ, jeśli przygotuję parametry tak aby stanowiły je puste Stringi to kod działa bez zarzutu. Po prostu najzwyczajniej kod oblicza, że pierwszy parametr występuje po "pierwszym znaku" w tekście, który jest drugim parametrem (pustym Stringiem). No i to się zgadza z punktu widzenia kodu, niestety nijak nie testuje to kodu ponieważ, raz jeszcze, aplikacja rzuca ArrayIndexOutOfBoundException gdy jest odpalona z pustą listą.
Nie mogę też zadeklarować, że lista jest nullem, to skutkuje NullPointerem.
A w takim wypadku nie mam pojęcia jak inaczej miałbym zadeklarować parametry tak, aby test wykazał rzeczywisty rezultat wywołania kodu.

YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:5 dni
  • Postów:2370
1

@novice: Trochę poza tematem przewodnim... Chcę przetestować, że aplikacja rzuci ArrayIndexOutOfBoundsException w momencie gdy pierwszy, drugi lub obydwa parametry są puste. - serio masz taką aplikację, która rzuca tym wyjątkiem jak nie podasz właściwych parametrów? Z perspektywy szarego użytkownika jest to nieczytelna informacja. Przeważnie aplikacje produkują coś w stylu: usage: --help, -h for help, jeśli parametry są wymagane i użytkownik nie poda wymaganych.

Jeśli masz możliwość, to wysiłek włóż w poprawienie aplikacji, a nie w obejścia na potrzeby testów. Może warto rozdzielić "parsowanie" parametrów uruchomieniowych od biznesowego użycia tychże?

NO
  • Rejestracja:prawie 4 lata
  • Ostatnio:ponad 3 lata
  • Postów:6
0

@yarel: Nie ja jestem twórcą tej aplikacji, a sam testuję ją w ramach ćwiczeń. Gdyby to była moja apka to inaczej rozwiązałbym kwestię tego wyjątku. Poza tym tak na prawdę nie chodzi mi o przetestowanie tego konkretnego wyjątku, a jedynie poprawne przetestowanie, że aplikacja się wysypuje jeśli jeden lub obydwa argumenty są puste. Stąd wydaje mi się, że przetestowanie tego wyjątku byłoby najbardziej sensowne, bo nie widzę innej możliwości.

AK
  • Rejestracja:prawie 7 lat
  • Ostatnio:około miesiąc
  • Postów:3561
0

Właśnie @yarel w pełni sie zgadzam, coś mi nie gra pod względem zaangażowania energii.

Nie rozumiem @novice dlaczego

Kopiuj

String[] args = ...

AnEntryClass.main(args)

nie miałoby zaskutkować


Bo C to najlepszy język, każdy uczeń ci to powie
edytowany 1x, ostatnio: AnyKtokolwiek
NO
  • Rejestracja:prawie 4 lata
  • Ostatnio:ponad 3 lata
  • Postów:6
0

@yarel: @Shalom: @AnyKtokolwiek: Dobra, nie jestem zbyt bystry... Z uporem maniaka chciałem "coś" wepchnąć do tej listy parametrów. Albo puste Stringi albo nulle, stąd te niechciane wyniki. A wystarczyło zadeklarować tablicę parametrów bez tworzenia instancji lub stworzyć instancję tablicy o długości 0...
Dziękuję w każdym razie za wszystkie wskazówki, aż mi głupio z powodu tej ignorancji :/

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.