• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Czy powinienem to testować - testy jednostkowe JUnit

0 głosów
591 wizyt
pytanie zadane 2 sierpnia 2018 w Java przez must Bywalec (2,980 p.)

Cześć. Postanowiłem się pouczyć trochę o testach jednostkowych i oczywiście kilka z nich napisać, ale tutaj pojawia się pytanie. Jakiego typu metody powinienem testować?

Na przykładzie, mam taką klasę:

package CarRental;

import java.sql.SQLException;
import java.util.InputMismatchException;
import java.util.Scanner;

import CarRental.DataGetter.ClientDataGetter;
import CarRental.DataGetter.WorkerDataGetter;


public class CarRentalEngine {

    private int option;
    private Scanner input = new Scanner(System.in);
    private CarRentalOptions carRentalOptions = new CarRentalOptions();
    private ClientDataGetter clientDataGetter = new ClientDataGetter();
    private WorkerDataGetter workerDataGetter = new WorkerDataGetter();

    CarRentalEngine() throws SQLException {
    }

    void startCarRental() throws SQLException {
        System.out.println("Who are you?\n1. Customer\n2. Worker");
        try {
            switch (input.nextInt()) {
                case 1:
                    executeClientCase();
                    break;
                case 2:
                    executeWorkerCase();
                    break;
            }
        } catch (InputMismatchException e) {
            System.err.println("Your input is wrong!");
        }
    }


    void executeOptionsForClient(int option) throws SQLException {
        switch (option) {
            case 1:
                carRentalOptions.rentACar(clientDataGetter.rentACar(input));
                break;
            case 2:
                carRentalOptions.returnACar(clientDataGetter.returnACar(input));
                break;
            case 3:
                carRentalOptions.populateTableRent(clientDataGetter.populateTableRent(input));
                break;
            case 4:
                carRentalOptions.populateTableViewCars(clientDataGetter.populateTableViewCars(input));
                break;
            case 5:
                break;
        }
    }


    void executeOptionsForWorker(int option) throws SQLException {
        switch (option) {
            case 1:
                carRentalOptions.populateTableViewClients(input);
                break;
            case 2:
                carRentalOptions.populateTableViewCars(clientDataGetter.populateTableViewCars(input));
                break;
            case 3:
                carRentalOptions.makeCarAvailable(workerDataGetter.makeCarAavailable(input));
                break;
            case 4:
                carRentalOptions.makeCarUnavailable(workerDataGetter.makeCarUnavailable(input));
                break;
            case 5:
                carRentalOptions.createNewCar(workerDataGetter.createCar(input));
            case 6:
                break;
        }
    }


    void executeClientCase() throws SQLException {
        System.out.println("1. Have you inputted your data before?\nN/Y: ");
        if (input.next().toUpperCase().equals("N")) {
            carRentalOptions.createNewCustomer(clientDataGetter.createClient(input));
            System.out.println("Now you have your unique number clinet, use it where it is required!");
        } else {
            do {
                System.out.println("What do you want to do?");
                System.out.println("1. Rent a car\n2. Return a car\n3. Populate rented cars\n4. Populate cars\n5. Quit");
                option = input.nextInt();
                executeOptionsForClient(option);
            }
            while (option != 5);
        }
    }

    void executeWorkerCase() throws SQLException {
        do {
            System.out.println("What do you want to do?");
            System.out.println("1. Populate clients\n2. Populate cars\n3. Make car available\n4. Make car unavailable\n5. Insert new car\n6. Quit");
            option = input.nextInt();
            executeOptionsForWorker(option);
        }
        while (option != 6);
    }
}

Jak przetestować metody: 

executeWorkerCase, executeClientCase, executeOptionsForWorker, startCarRental

Tak samą mam taką klasę:

package CarRental;

import CarRental.Model.Car;
import CarRental.Model.Client;
import CarRental.Model.RentingACar;
import DB.DataBase;

import java.sql.SQLException;
import java.util.Scanner;


public class CarRentalOptions {
    private DataBase dataBase = new DataBase();

    CarRentalOptions() throws SQLException {
    }

    void createNewCustomer(Client client) throws SQLException {
        dataBase.insertNewCustomer(client);

        System.out.println("Client added successfully!");
    }

    void createNewCar(Car car) throws SQLException {
        dataBase.insertNewCar(car);

        System.out.println("Car added successfully!");
    }

    void makeCarUnavailable(Car car) throws SQLException {
        dataBase.makeCarUnavailable(car);
    }

    void makeCarAvailable(Car car) throws SQLException {
        dataBase.makeCarAvailable(car);
    }

    void rentACar(RentingACar rentingACar) throws SQLException {
        dataBase.rentACar(rentingACar);
    }

    void populateTableViewCars(Car car) throws SQLException {
        dataBase.populateTableViewCars(car);
    }

    void populateTableRent(Client client) throws SQLException {
        dataBase.populateTableRent(client);
    }

    void populateTableViewClients(Scanner input) throws SQLException {
        dataBase.populateTableViewClients();
    }

    void returnACar(Car car) throws SQLException {
        dataBase.returnACar(car);
    }
}

Tutaj też nie wiem czy powinienem to testować.

 

5 odpowiedzi

+1 głos
odpowiedź 4 sierpnia 2018 przez RafalS VIP (122,860 p.)
wybrane 4 sierpnia 2018 przez must
 
Najlepsza

Odpowiadając na pytania, które pojawiły się w komentarzach:

Czemu nie da się tego przetestować?

Złe podejście, nie da się podmienić bazy danych dynamicznie podczas wykonywania programu na czas testowania:

public class CarRentalOptions {
    private DataBase dataBase = new DataBase();
 
    CarRentalOptions() throws SQLException {
    }
}

Dlatego ktoś wymyślił dependency injection (wstrzykiwanie zależności), np poprzez konstruktor:

public class CarRentalOptions {
    private DataBase dataBase;
 
    CarRentalOptions(DataBase dataBase) {
         this.dataBase = dataBase;
    }
}

W tym momencie zyskaliśmy tylko częśc tego co chcemy osiągnąć - możemy przesyłać klasie CarRentalOptions skonfigurowane obiekty klasy DataBase. Nie jest źle, ale na potrzeby testowania potrzebujemy wstrzyknąć tam całkiem inną klase niż DataBase. Dlatego żeby to zadziałało potrzeba też zmienić typ składowej dataBase tak żeby mogły się tam znaleźć różne implementacje baz danych. Ale musimy się upewnić że wstrzykiwane implementacje będa miały odpowiednie metody na których będziemy w tej klasie polegać np dataBase.add(...), dlatego dobrym podejściem jest stworzenie interfejsu:

interface CarRentalStorage{
    void addCustomer(Customer customer);
    List<Customer> getAllCustomers();
    void deleteCustomer(Customer customer);
}

class CarRentalSQLDatabase implements CarRentalStorage{

    @Override
    public void addCustomer(Customer customer) {
        //database add logic
    }

    @Override
    public List<Customer> getAllCustomers() {
        //database read logic
    }

    @Override
    public void deleteCustomer(Customer customer) {
        //database delete logic
    }
}

class CarRentalFileStorage implements CarRentalStorage{

    @Override
    public void addCustomer(Customer customer) {
        //file add logic
    }

    @Override
    public List<Customer> getAllCustomers() {
        //file read logic
    }

    @Override
    public void deleteCustomer(Customer customer) {
        //delete from file logic
    }
}

class CarRentalOptions {
    private CarRentalStorage storage;

    CarRentalOptions(CarRentalStorage storage) {
        this.storage = storage;
    }
}

Interfejs powininen być maksymalnie generyczny, tak żeby nieświadomie nie uzależnić się od konkretnej implementacji np metoda connect w takim interfejsie ograniczyłaby nas do różnego typu baz danych. Z generycznością moglibyśmy też przesadzić tworzać interfejs DataStorage, który operowałby na Objectach. Trzeba znaleźć złoty środek w maksymalizowaniu spójności i minimalizowaniu zależności (low coupling and high cohesion, interface segretation principle (SOLID)).

Ale wróćmy do tematu. Co nam to wszystko dało. Po pierwsze teraz Twoja klasa CarRentalOptions jest bardziej elastyczna, t.j. potrafi współpracować z różnymi wersjami przechowywania danych. Obchodzi ją tylko, żeby obiekt storage implementował odpowiednie metody, z których będzie korzystać. Po drugie możemy wreszcie napisać prosty test:

class CarRentalOptions {
    private CarRentalStorage storage;

    CarRentalOptions(CarRentalStorage storage) {
        this.storage = storage;
    }

    public void addCustomer(Customer customer){
        //some logic, prive methods calls...
        storage.addCustomer(customer);
    }

    public boolean isCustomerRegistered(Customer customer){
        return storage.getAllCustomers().contains(customer);
    }

}

@RunWith(MockitoJUnitRunner.class)
class CarRentalOptionsTest{
    CarRentalOptions objUnderTests;

    @Test
    public void addedCustomerShouldBeSaved(){
        //tworzymy "mocka" dynamicznie programowalny obiekt
        CarRentalStorage storageMock = mock(CarRentalStorage.class);
        //wstrzykujemy mocka
        objUnderTests = new CarRentalOptions(storageMock);

        Customer customer = new Customer();
        objUnderTests.addCustomer(customer);
        //upewniamy sie ze obiekt zostal dodany do przechowalni (storage)
        //ze zostal wywolany storage.addCustomer(customer)
        verify(storageMock).addCustomer(customer);

        //ustawiamy zachowanie mocka, obiekt dodany wiec getAllCustomers
        //powinien zwrocic liste z dodanym customerem
        List<Customer> customers = new ArrayList<>();
        customers.add(customer);
        when(storageMock.getAllCustomers()).thenReturn(customers);
        //faktyczny test, czy dodany customer zostal dodany do storage
        //nie obchodzi nas co robi z nim storage, testujemy jedynie obecna klase
        //dlatego testujemy tylko czy zostala wywolana odpowiednia metoda na 
        //skladowej storage
        Assert.assertTrue(objUnderTests.isCustomerRegistered(customer));
    }
    
}

Mocki można tworzyć i wskrzykiwać automatycznie (adnotacjami @Mock @InjectMocks), ale dla prostoty przykładu zrobiłem wszystko ręcznie.

To może na koniec powiem czemu mocki są takie popularne.

Wyobraź sobie, że mamy duży projekt. Testów jest tysiące. Puszczane są bardzo często (CI). Powiedzmy, że nie mockowaliśmy bazy danych tylko wszędzie korzystaliśmy z faktyczniej bazy danych. Setki klas w projekcie korzysta z tej jednej klasy obslugujacej baze danych. Nagle ktoś nieświadomie psuje klase obsługująca baze danych i wszystkie testy przestają przechodzić. Nikt nie wie co się stało. Panika. Do biznesmenów lecą raporty, że wszystkie testy są na czerwono i nie wytłumaczysz im, że ktoś nie zamockował, albo, że to tylko jedna klasa nawaliła a wszystko działa. Zamykają projekt. Zwalniają zespół. Troche lipa :D

komentarz 4 sierpnia 2018 przez must Bywalec (2,980 p.)
hej, dzięki za odpowiedź! W wolnej chwili zerknę na pewno, ale jest to pierwsza konkretna odpowiedź, której oczekiwałem. Dzięki!.
komentarz 5 sierpnia 2018 przez RafalS VIP (122,860 p.)
Generalnie polecam też zobaczyć jakieś przykłady mockowania, bo ja jedynie zarysowałem o co z tym chodzi, ale Mockito ma wiele innych funkcjonalności. Przeważnie korzysta się z kilku najważniejszych, ale dobrze wiedzieć jakie są możliwości.

Polecam przykłady z oficjalnego tutoriala mockito

http://static.javadoc.io/org.mockito/mockito-core/2.20.0/org/mockito/Mockito.html
komentarz 11 sierpnia 2018 przez must Bywalec (2,980 p.)

Ogarnąłem temat mocków, mniej więcej ogarniam temat.

Mimo to podtrzymuje dalej pytania:

1) Jak dość do tego by zwracało mi listę klientów jako liste nie tekst

2) 

public boolean isCustomerRegistered(Customer customer){
        return storage.getAllCustomers().contains(customer);
    }

Ta klasa mi nie pasuje w CarRentalOptions, bo ma to się troche nijak do tego co aktualnei zbudowałem. Czy nie miało by to działać właśnie na zasadzie stuba? Czyli w osobnej całkowicie klasie?

3) Jak rozegrać ten pakunek DataGetter i przykładową metodę createClient


public Client createClient(Scanner input) {
        Client client = new Client();
        client.setClientNumber(rand.nextInt(999));
 
        System.out.print("name: ");
        client.setName(input.next());
        System.out.print("surname: ");
        client.setSurname(input.next());
        System.out.print("city: ");
        client.setCity(input.next());
        System.out.print("house number: ");
        client.setHouseNumber(input.nextInt());
        System.out.print("street: ");
        client.setStreet(input.next());
        System.out.print("pesel number: ");
        client.setPeselNumber(input.nextLong());
        System.out.print("rent date: ");
        client.setRentDate(input.next());
        System.out.println("Your client number is: " + client.getClientNumber());
 
        return client;
    }

 

komentarz 12 sierpnia 2018 przez must Bywalec (2,980 p.)

Chyba sobie odpowiedziałem na 1)

Zrobiłem coś takiego:

@Override
    public List<Client> getAllCustomers() throws SQLException {
        List<Client> listOfClients = new ArrayList<Client>();

        String sql = "SELECT * FROM `client`";
        result = statement.executeQuery(sql);

        while (result.next()) {
            Client client = new Client();
            client.setName(result.getString("namee"));
            client.setSurname(result.getString("surname"));
            client.setStreet(result.getString("street"));
            client.setPeselNumber(result.getLong("peselNumber"));
            client.setRentDate(result.getString("rentDate"));
            client.setCity(result.getString("city"));
            client.setHouseNumber(result.getInt("houseNumber"));
            client.setClientNumber(result.getInt("clientNumber"));

            listOfClients.add(client);
        }

        return listOfClients;
    }

I CarRentalOptions

void getAllCustomers() throws SQLException {
        for (int i = 0; i < storage.getAllCustomers().size(); i++) {
            System.out.println("Name: " + storage.getAllCustomers().get(i).getName()
                    + "\nSurname: " + storage.getAllCustomers().get(i).getSurname()
                    + "\nStreet: " + storage.getAllCustomers().get(i).getStreet()
                    + "\nHouse number: " + storage.getAllCustomers().get(i).getHouseNumber()
                    + "\nCity: " + storage.getAllCustomers().get(i).getCity()
                    + "\nPesel Number: " + storage.getAllCustomers().get(i).getPeselNumber()
                    + "\nRent Date: " + storage.getAllCustomers().get(i).getRentDate()
                    + "\nClient number: " + storage.getAllCustomers().get(i).getClientNumber());
            System.out.println("---------------------------");
        }
    }

Co myślisz?

komentarz 12 sierpnia 2018 przez must Bywalec (2,980 p.)

2)

W taki sposób test zrobiłem:

@Test
    void createNewCustomer() throws SQLException {
        CarRentalStorage carRentalStorageMock = mock(CarRentalStorage.class);

        CarRentalOptions carRentalOptions = new CarRentalOptions(carRentalStorageMock);
        Client client = new Client();

        carRentalOptions.createNewCustomer(client);

        List<Client> listOfClients = new ArrayList<Client>();
        listOfClients.add(client);

        verify(carRentalStorageMock).addClient(client);

        when(carRentalStorageMock.getAllCustomers()).thenReturn(listOfClients);

        assertEquals(1, listOfClients.size());
    }

na 3) Raczej już nie znajdę odpowiedzi :P

komentarz 16 sierpnia 2018 przez RafalS VIP (122,860 p.)
Nie za bardzo mam czas, żeby to analizować i nie będzie mnie na forum jakiś czas. Zadaj kolejne pytanie, na pewno ktoś pomoże :)
+2 głosów
odpowiedź 2 sierpnia 2018 przez marcin99b Maniak (71,590 p.)
Kiedyś spotkałem się z zasadą że jeśli to nie jest punkt przez który przechodzi masa wartości biznesowej - przykładowo decyzja czy automatycznie zwrócić pieniądze za produkt, albo punkt gdzie przechodzi dużo danych

A ewentualna naprawa zajmie mniej niż 15 minut, bo ewentualnym błędem może być literówka albo szczegół (mamy prosty kod)
To nie warto tego testować, ponieważ nie opłaca się porównując straty do kosztów czasu programisty

Ale jeśli chodzi o własne aplikacje, po prostu rób to na wyczucie, co według ciebie nie jest pewne że zadziała -> testuj
Albo co zawsze musi działać w 100% -> testuj

Zamiast testów jednostkowych możesz też wspomagać się testami end to end, które co prawda nie pokażą ci dokładnie która metoda której klasy się wywala, przy jakich wartościach, z informacją dlaczego się wywala
Ale dzięki nim możesz sprawdzić czy pewien obszar -działa

Przykładowo - nie sprawdzisz która część kodu odpowiedzialna za rejestracje nie działa, ale jesteś w stanie sprawdzić czy ogólnie rejestracja działa lub nie

Pamiętaj tylko żeby pod testy end to end podpiąć inną baze niż tą "produkcyjną" (ConnectionString powinien być w jakimś zewnętrznym pliku, np json, a nie w skompilowanym kodzie, tak przynajmniej robimy w .net)

Jeszcze taka rada
Pamiętaj o wzorcu strategia i ogólnie o możliwości podmiany interfejsów (do tego dependency injection i te sprawy)
Pozwala na dużo prostsze testowanie jednostkowe, bo nie trzeba aż tak kombinować
Między innymi dlatego, że łatwo to mockować - prościej symulować zdarzenia na obiektach, w testach
1
komentarz 2 sierpnia 2018 przez Tomek Sochacki Ekspert (228,720 p.)

Albo co zawsze musi działać w 100% -> testuj 

A skoro jest w kodzie coś (funkcja, klasa itp.) co nie musi działać zawsze to po co w ogóle to tam jest...?

Dużo w podejściu do testowania zmienia przejście na system TDD...

komentarz 2 sierpnia 2018 przez marcin99b Maniak (71,590 p.)
Zależy od interpretacji w sumie

Są elementy bardziej wartościowe i mniej
Na pewno więcej szkody wyrządzi uszkodzony kontener IoC, bez którego prawie cała aplikacja leży, niż problemy z aktualizacją miejscowości użytkownika, która jest informacją dla innych, a żadne operacje sie na tym nie wykonują
komentarz 3 sierpnia 2018 przez Secrus Nałogowiec (32,880 p.)
Przykład co nie musi działać w 100%:

Wysyłanie do użytkownika emaila powitalnego nie dającego mu żadnej informacji poza tym, że serwis się cieszy i dziękuje za rejestracje itp.
 

Co musi działać w 100%:

Przepływ informacji o nowym użytkowniku do bazy, szczególnie jeśli w aplikacji obracamy w jakiś sposób piniędzmi
komentarz 3 sierpnia 2018 przez Tomek Sochacki Ekspert (228,720 p.)

Przykład co nie musi działać w 100%:
Wysyłanie do użytkownika emaila powitalnego nie dającego mu żadnej informacji poza tym, że serwis się cieszy i dziękuje za rejestracje itp.

A to już zależy... jeśli Product Owner w Twoim projekcie uzna, że nie jest to faktycznie 100% obowiązkowa funkcjonalność to oki, ale raczej częściej takie właśnie rzeczy są dla "góry" ważniejsze niż niektóre tematy "pod spodem API"... 

No i to jest troszkę miejsce, gdzie czasami dochodzi do zgrzytu back-end - front... coś co dla back-endowca jest funkcjonalnością "nic nie robiącą" dla fronta i marketingu jest obowiązkową :)

Także zawsze trzeba szukać kompromisów...

komentarz 4 sierpnia 2018 przez marcin99b Maniak (71,590 p.)
No właśnie, zawsze trzeba na to spojrzeć z poziomu "biznesu"

Jeśli koszty nie działania tego (np jedno z opcjonalnych pól tekstowych) są niższe niż koszty testowania tego i przewidywania przypadków dla których to może działać/nie działać
To finansowo nie opłaca się tego robić

A jeśli nie działanie czegoś sprawi, że 100% użytkowników nie będzie mogło używać 80% aplikacji, bo repozytorium ogłoszeń w aplikacji ogłoszeniowej przestanie działać
To znaczy, że koszty kazania programiście napisania testu są dużo niższe niż ewentualny błąd w kodzie

Sporo zależy też od skomplikowania kodu
Są sytuacje gdzie jakiś szczegół można zostawić bo na 99% działa a tworzenie nowego testu to dodatkowy czas pracy, w dodatku ten element jest z tych "mniej ważnych"
A są sytuacje gdzie jakiś element jest napisany kodem spaghetti i w sytuacji gdzie użytkownik zgłosi błąd, naprawa potencjalnie prostej funkcjonalności nie zajmie max 15 minut, tylko trochę (tu wstaw losową liczbę) więcej
+2 głosów
odpowiedź 2 sierpnia 2018 przez Ehlert Ekspert (205,650 p.)
Zacznijmy od tego że dla tego kodu nie powinieneś zaczynać od testów, ale od refactoru. Ta klasa robi wszystko: połączenie z bazą danych, scanner i ten zagadkowy switch. Polecam poczytać książki Flowera.
1
komentarz 2 sierpnia 2018 przez RafalS VIP (122,860 p.)
Pisanie testów jedostkowych jest wg mnie bardzo powtarzalne, dlatego polecam znaleźć jakiś projekt na githubie, który ma napisane testy, popatrzeć na to i od razu Ci się rozjaśni o czym wszyscy mówimy z tymi mockami i dependency injection.
komentarz 2 sierpnia 2018 przez must Bywalec (2,980 p.)

I była prywatna, wszystkie był prywatne oprócz 

startCarRental

ale nie mogłem ich przetestować dlatego usunąłem private.

Właśnie oglądam jakieś tutoriale o tym Mockito. Ale czytając to co napisałes, to jest to dla mnie czarna magia i chyba porywam się z motyką na słońce :P

Dependency Injection, kolejny mój problem, oglądałem masę filmików, dalej nie potrafię tego zaimplementować w tej wypożyczalni.

1
komentarz 3 sierpnia 2018 przez RafalS VIP (122,860 p.)
Napisze Ci to / znajde dobre przyklady jak tylko na miał czas
komentarz 3 sierpnia 2018 przez must Bywalec (2,980 p.)
Dzięki.
1
komentarz 4 sierpnia 2018 przez RafalS VIP (122,860 p.)
Napisałem coś o dependency injection i mockach w odpowiedzi, rzuć okiem.
0 głosów
odpowiedź 2 sierpnia 2018 przez must Bywalec (2,980 p.)
Czyli co testować - wszystko. :D

A jeżeli chodzi o to, jak przetestować metody które podałem w pytaniu?
–1 głos
odpowiedź 2 sierpnia 2018 przez Mateusz51 Nałogowiec (28,220 p.)
Na początku możesz testować dosłownie wszystko. Idąc z czasem zdobędziesz doświadczenie mówiące Ci co jest warto testować a co nie.

Tak na szybko. Nie warto testować get i set oraz mały sens ma testowanie połączeń z systemami zewnętrznymi, kłóci się to trochę z założeniami testów jednostkowych.
komentarz 2 sierpnia 2018 przez must Bywalec (2,980 p.)
Czyli mam nie testować baz danych?

Czy w 2 klasach, które wrzuciłem powinienem przetestować wszystkie metody?

Jak mam metodę executeWorkerCase  w klasie CarRentalEngine, to co powinienem dokładnie przetestować i jak to zrobić, bo nawet nie wiem co mam wpisać w google.
komentarz 2 sierpnia 2018 przez Tomek Sochacki Ekspert (228,720 p.)

 Nie warto testować get i set 

No tu bym dyskutował... jeśli jest tego niewiele to jaki problem napisać 1-2 dodatkowe testy? A jeśli korzystamy w jakieś klasie nadmiernie z get/set to zastanowiłbym się nad tym, czy na pewno wszystko w klasie jest ok...

Moim zdaniem nie warto oszczędzać na testach, bo gdy wejdą kolejny programiści to wszystko może szybko runąć...

komentarz 2 sierpnia 2018 przez Mateusz51 Nałogowiec (28,220 p.)
A jaki jest sesn pisania testu który sprawdza czy ustawia się zmienna? Jest to strata czasu programisty i zaciemnianie kodu. Złe testy są gorsze od żadnych.

Get i set można przetestować globanie testując cały proces.
1
komentarz 2 sierpnia 2018 przez Tomek Sochacki Ekspert (228,720 p.)
a co, jeśli jakiś setter czy getter wykonuje dodatkowo jakąś logikę? Jak już upierać się przy nietestowaniu tego to obstawiałbym za brakiem jakiejkolwiek dodatkowej logiki w tych metodach...
komentarz 2 sierpnia 2018 przez Mateusz51 Nałogowiec (28,220 p.)
No zakładam że nie ma tam logiki
komentarz 2 sierpnia 2018 przez Tomek Sochacki Ekspert (228,720 p.)

No zakładam że

Gdy nad projektem pracuje kilka osób to oki... ale gdy w zespole są rotacje ludzi iprojekt ciągnie się przez długi czas to uważałbym z takimi założeniami... :)

Ale oczywiście w małych projektach, krótkich itp. można podjąć takie założenia, ale powinny one być poparte np. ogólymi zasadami pisania kodu danej apki i wyłapywane w code review, bo inaczej może być kiedyś problemik :)

Wszystko zalezy więc od przypadku, ale warto chociaż analizować takie tematy.

komentarz 2 sierpnia 2018 przez Mateusz51 Nałogowiec (28,220 p.)
Nie chce się kłócić. Ale jak ktoś umieszcza logike w setterach to raczej testy mu nie pomogą
1
komentarz 2 sierpnia 2018 przez Aisekai Nałogowiec (42,270 p.)
edycja 2 sierpnia 2018 przez Aisekai
Logika w setterach ... Czemu nie wolno umieszczac? Według mnie nie ma w tym nic zlego, a wręcz przeciwnie - mocno ułatwia pisanie kodu. Czemu?

-Pierwsza sytuacja gdy Ty piszesz kod i musisz w wielu miejscach ustawic jakieś pole jakas wartością. Duzo prościej i bezpieczniej jest gdy klasa sama sobą zarzadza.  

-Druga sytuacja, gdy ktoś pisze kod to może nie wiedzieć, ze musiała coś wykonac przed użyciem settera i ustawi np hasło na bardzo słabe.

-Wedlug mnie, takie podejście jest troche sprzeczne z OOP i pozbywajac się logiki z klas reprezentujących jakies dane, możesz doprowadzic do anemicznych klas.

Edit: Używanie setterow i getterow, jeżeli framework nie narzuca ich utworzenia, w których sie zakłada ze nie bedzie żadnej logiki mija sie z celem. Trzymanie logiki w setterach jest o tyle lepsze, że:

Załóżmy taką sytuację, że nie ma potrzeby walidowania jakiegoś pola, więc na chwilę obecną nie ma walidacji w całej aplikacji dla tego pola. Po jakims czasoe weszła ustawa, że jednak musi mieć specyficzny format. Dużo łatwiej w jednym miejscu dodac walidację niz szukać po całej aplikacji gdzie ustawiasz to pole.
1
komentarz 2 sierpnia 2018 przez Aisekai Nałogowiec (42,270 p.)
A co do testowania polaczenia z baza danych: ma to sens. Tylko nie sa to testy jednostkowe, a testy integracyjne.

Podobne pytania

0 głosów
1 odpowiedź 68 wizyt
pytanie zadane 4 maja 2021 w Java przez janyczek Początkujący (360 p.)
0 głosów
0 odpowiedzi 110 wizyt
pytanie zadane 24 listopada 2019 w Java przez JuniorPL Użytkownik (770 p.)
0 głosów
2 odpowiedzi 244 wizyt
pytanie zadane 11 stycznia 2016 w Java przez natrov Gaduła (3,970 p.)

86,427 zapytań

135,187 odpowiedzi

300,309 komentarzy

57,184 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...