Klasy testów jednostkowych, które mają funkcjonalność online

23

Podczas testowania jednostkowego funkcji klasy, która ma funkcje prywatne wymagające funkcjonalności online. Jak można przejść do testowania?

Na przykład:

public class Foo
{
    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        CloudService c = new CloudService();
        int oval = c.getValueFromService();
        return oval;
    }
}

Gdybym miał przetestować funkcję: „methodA ()”, próbowałby użyć „goOnlineToGetVal ()”, która z kolei spróbowałaby przejść do trybu online, gdyby test został wykonany bez funkcjonalności. Jak przejść około 100% pokrycia klasy bez przechodzenia do trybu online?

Ben Crazed Up Euden
źródło
Czy twoja metoda po prostu wywołuje api w chmurze, czy też wykonuje dodatkową pracę?
Winston Ewert
4
Wstrzykiwanie zależności?
PhaDaPhunk

Odpowiedzi:

76

new CloudService()

I jest twój problem.

Nowoczesny projekt OO zaleca przekazywanie tego rodzaju zależności, a nie konstruowanie ich bezpośrednio. Można to przekazać do samej funkcji lub do klasy w czasie budowy. Może być również przechwycony lub zagregowany przez kontener Inwersja Kontroli, jeśli taka złożoność jest uzasadniona.

W tym momencie przechodzenie przez fałszywą / fałszywą usługę, aby zapewnić ci swoje dane „online” podczas testowania, staje się dość trywialne. Co więcej, pozwala na to, aby twoje oprogramowanie było wystarczająco elastyczne, abyś mógł szybko się dostosować, gdyby pojawił się jakiś (rządowy?) Klient i nie chce używać chmury dla swoich wartości. Lub chcesz zrzucić jednego dostawcę chmury dla drugiego. Lub...

Telastyn
źródło
7
Myślę, że właśnie zbliżyłem się do zrozumienia, co to do cholery jest Dependency Injection.
marczellm
Gdzie jest najlepsze miejsce do stworzenia wszystkich instancji, których potrzebuje moja aplikacja? Jak najbliżej początku mojej aplikacji? Do tej pory można stosować wyłącznie wstrzykiwanie zależności; w pewnym momencie będziesz musiał sam utworzyć instancje zamiast przekazywać je jako argumenty.
Paul
3
@Paul - To zależy. Z mojego doświadczenia wynika, że ​​aplikacje bardzo często wyglądają jak drzewa binarne - jedna klasa / funkcja / moduł skleja dwa, aby zapewnić bardziej złożone zachowanie. Jedną z tych klas „kleju” jest ta, która określa konkretną implementację, która z kolei jest używana przez coś powyżej, co skleja ją z czymś innym, co z kolei ... aż w końcu dojdziesz do punktu wejścia, który skleja duże kawałki razem spójnie. „Najlepsze” miejsce to miejsce, które pozwala Twojemu kodowi robić to, co powinien, bez zmuszania go do robienia rzeczy i nie wiedzieć o tym, czego nie powinien. Będzie się różnić.
Telastyn
2
@Paul Zwykle twoja aplikacja jest podzielona na „bibliotekę”, która jest bezpośrednio testowana i powinna używać tego rodzaju wstrzykiwania zależności, oraz kod specyficzny dla aplikacji. Ten ostatni zwykle sprowadza się do jakiegoś GUI, usługi internetowej lub interfejsu wiersza poleceń, który bezpośrednio wywołuje bibliotekę z danymi dostarczonymi przez użytkownika. Zasadniczo kod specyficzny dla aplikacji utworzy konkretne implementacje, których oczekuje biblioteka wstrzykiwana w zależności.
Darkhogg,
@Paul Spójrz na pojemniki IoC, takie jak Castle Windsor, StructureMap, Ninject i Unity. Nie są one wcale jedynym sposobem na wstrzyknięcie zależności, ale mogą być bardzo pouczające, jeśli chodzi o myślenie o konstruowaniu wykresu obiektowego, z góry na dół
Ben Aaronson
37

Zaimplementowałbym to w ten sposób:

public class Foo
{
    private ICloudService cloudService;

    public Foo(ICloudService s)
    {
       cloudService=s;
    }

    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        int oval = cloudService.getValueFromService();
        return oval;
    }
}

Interfejs ICloudServicemożna zaimplementować za pomocą próbnego narzędzia do testowania lub za pomocą „prawdziwej” usługi w chmurze. Jeśli tworzenie instancji new CloudService()jest obowiązkowe dla każdego wywołania getValueFromServicelub CloudServicepochodzi z interfejsu API innej firmy, którego nie można zmienić, zaimplementuj opakowanie, wywodząc ICloudServicei wykonując odpowiednie połączenia.

Doktor Brown
źródło
1
To zdecydowanie najlepsza droga. Istnieją biblioteki klas, które mogą kpić z interfejsu, co daje absolutną kontrolę nad testem.
Greg Burghardt