Länge eines Arrays in einem Objekt testen

Diskutiere Länge eines Arrays in einem Objekt testen im Java Basics - Anfänger-Themen Bereich.
N

nikva

wenn ich das jetzt so vergleiche kriege ich eine NPE. Liegt wohl daran dass shops nochnicht erzeugt wurde?
Java:
package edu.hm.cs.swe2.shoppingmall;

import static org.junit.Assert.assertEquals;

import org.junit.jupiter.api.Test;

public class ShoppingMallTest {

    @Test
    public void testShoppingMall() {
        int shopCountTest = 0;
        // Testwert bestimmen
        ShoppingMall testShoppingMall = new ShoppingMall(shopCountTest);
        // erzeugt ein Objekt ShoppingMall mit Testwert
        int expectedResult = 10;
        // zu erwartende Länge des Array der durch Shoppingmal erzeugt wird mit Testwert
        // 0
        int actualResult = testShoppingMall.size();

        assertEquals(expectedResult, actualResult);
    }

}
 
J

JustNobody

Ja, das ist Sinn der Unit-Tests. Du hast einen Fehler gefunden und musst den nun beheben.

Schauen wir uns einmal diesen Code an:
Code:
    public ShoppingMall(int shopCount) {
        if (shopCount < 1 || shopCount > 500) {
            shopCount = 10;
        } else {
            this.shops = new Shop[shopCount];
        }
    }

Überlege einmal genau, was passiert, wenn shopCount < 1 oder > 500 ist. Was ist dann shops?
 
N

nikva

ok ich sehe es. wenn eine der beiden Bedingungen erfüllt ist, dann wird shops nicht erzeugt, weil der else zweigt nicht erreicht wird. Ich muss also auch im if zweig mit this.shops = new Shop[10]; direkt das shops array erzeugen
 
N

nikva

Ich habe jetzt alle Klassen fertig und will jetzt die restlichen Tests erstellen. In der Aufgabenstellung heißt es, dass alle "relevanten Methoden" getestet werden sollen. Void Methoden wurden ausgeschlossen, aber getter,Setter und Konstruktoren ohne Bedingungen zu testen ist auch nicht besonders sinnvoll und fällt wohl auch nicht unter "relevante Methoden", oder?
 
L

LimDul

Ich habe jetzt alle Klassen fertig und will jetzt die restlichen Tests erstellen. In der Aufgabenstellung heißt es, dass alle "relevanten Methoden" getestet werden sollen. Void Methoden wurden ausgeschlossen, aber getter,Setter und Konstruktoren ohne Bedingungen zu testen ist auch nicht besonders sinnvoll und fällt wohl auch nicht unter "relevante Methoden", oder?
Schwierig zu beantworten. Ich gebe dir jetzt mal an, wie ich es aus der Praxis sehen würde - allerdings sind Uni-Aufgaben nicht immer gleich zur Praxis:

Relevant ist alles, wo selbstgeschriebener Code drin ist
Das heißt, alles was nicht generiert ist. Abstriche macht man in der Regel:
* Bei simplen gettern/settern (die also nur aus return this.variable; bzw. this.variable = variable; bestehen). Die sind ja auch oft quasi mit IDE Board-Mitteln generiert.
* Konstruktoren, da die zum einem meist nur aus Zuweisungen bestehen, zum anderen in der Regel von den restlichen Tests mit abgedeckt werden.
* Private Methoden, da diese nicht zur öffentlichen Schnittstelle gehören - und mit den Tests will ich ja sicherstellen, dass die öffentliche Schnittstelle korrekt arbeiten (Die werden in der Regel implizit mitgetestet)

Aber auch void-Methodne sind normalerweise relevant! Den auch diese Methoden machen etwas sinnvolles was man testen sollte. Wenn sie nichts tun, könnte man sie auch löschen.

Ziel sollte sein, eine möglichst Hohe Codeabdeckung (in Eclipse z.B. mittels EclEmma als Plugin messbar) zu erreichen.
 
W

White_Fox

Irgendwer sagte mir hier mal, daß ein Test eine Demonstration sein solle, wie mit einer Klasse zu arbeiten sei. Ich glaube, @mrBrown war das mal gewesen. Jedenfalls ist diese Betrachtung gar nicht so doof.

Simple setter- und Gettermethoden teste ich eigentlich auch immer mit, da es doch hin und wieder mal vorkommt daß die Methode geändert wird wenn man an der internen Implementierung herumpfuscht.

Allgemein sind Tests auch immer nützlich, um herauszufinden, ob eine Klasse nach außen hin noch genau dasselbe macht nachdem man z.B. intern viel geändert hat.

Ich z.B. sitze gerade an dem Problem, daß mir der Code in einer Klasse völlig ausufert. Der Test dieser Klasse ist recht umfangreich, da viele verschiedene Testszenarien, jedoch ist die Klasse auch >1.200 Codezeilen lang.
Da es mittlerweile schwierig wird, da noch die Übersicht zu behalten, andererseits neue Erweiterungen (mindestens eine steht noch an) immer hakeliger werden, baue ich die interne Struktur gerade völlig um.
Die Klasse ist intern sehr prozedural aufgebaut, jetzt lagere ich viel Code an kleinere, neue Klassen aus. Ursprünglich gab es für die interne Funktionsweise nur zwei Klassen, jetzt werden es sieben (und eine achte steht an).

Und mit dem Unittest kann ich jetzt testen, ob alles hinterher genauso funktioniert wie vorher. Nach solchen Umbauten kann ein Test nicht umfangreich genug sein. Daher: Teste ruhig mehr als weniger.
 
Zuletzt bearbeitet:
N

nikva

Klingt vernünftig.
Beim Testen von ToyStore(Aufgabe 6) habe ich jetzt das Problem, dass sich die Miete durch die geöffneten Stunden und die Stundenmiete berechnet.
Nun wird aber nicht bei jedem Toystore die Öffnungszeit angegeben,weswegen ich 2 Konstruktoren habe: einen mit und einen ohne Öffnungszeiten.
in Aufgabe 13 muss man jetzt einen Toystore ohne Öffnungszeiten erstellen:
e) Erzeugen Sie den Spielzeugladen „Cool Toys“ mit 150qm und 12,50 Miete. Der Laden öffnet um 9 Uhr und schließt um 17 Uhr.
f) Erzeugen Sie den Spielzeugladen „Cheap Toys“ mit 150qm und 5,50 Miete.
Beim Testen kriege ich jetzt aber logischerweise einen Fehler wenn ich den Testcase der ohne Öffnungszeiten erstellt wurde teste. Ist das ein Problem oder wurde das einfach in der Aufgabenstellung vergessen?

Java:
package edu.hm.cs.swe2.shoppingmall;

import static org.junit.Assert.assertEquals;

import java.time.LocalTime;

import org.junit.jupiter.api.Test;

class ToyStoreTest {
    @Test
    public void testgetRent() {
        ToyStore testToyStore = new ToyStore("Testshop", 300, 12.50, LocalTime.of(9, 0), LocalTime.of(15, 0));
        double expectedResult = 75.0;
        double actualResult = testToyStore.getRent();
        assertEquals(expectedResult, actualResult, 0);

        ToyStore testToyStore2 = new ToyStore("Testshop2", 300, 12.50);
        double expectedResult2 = 75.0;
        double actualResult2 = testToyStore2.getRent();
        assertEquals(expectedResult2, actualResult2, 0);
    }
}

Java:
package edu.hm.cs.swe2.shoppingmall;

import java.time.LocalTime;
import java.time.temporal.ChronoUnit;

public class ToyStore extends Shop {

    public ToyStore(String name, int area, double rent, LocalTime openingTime, LocalTime closingTime) {
        super(name, area, rent);
        this.openingTime = openingTime;
        this.closingTime = closingTime;
    }

    public ToyStore(String name, int area, double rent) {
        super(name, area, rent);
    }

    @Override
    public double getRent() {
        return rent * openingTime.until(closingTime, ChronoUnit.HOURS);
    }
}
 
mihe7

mihe7

Irgendwer sagte mir hier mal, daß ein Test eine Demonstration sein solle, wie mit einer Klasse zu arbeiten sei. Ich glaube, @mrBrown war das mal gewesen. Jedenfalls ist diese Betrachtung gar nicht so doof.
Das ist durchaus denkbar, dass er das war. Und ja, Tests dokumentieren die Verwendung :)
 
N

nikva

Gibt es eine Möglichkeit bei einem Test auf ein private attribut zuzugreifen ohne, dass ich einen Getter benutze?
 
N

nikva

Ich will in meinem Test ja manchmal auf die Attribute zugreifen und das geht nicht wenn die Sichtbarkeit auf private gesetzt ist.
 
T

thecain

Davon ging ich aus... Aber private Attribute müssen ja irgendwo verwendet werden, das public ist. Teste doch das, dann sollte die private Variable automatisch mitgetestet sein.
 
N

nikva

Java:
    Supermarket testSupermarket = new Supermarket("Testshop", 200, 50, 40);
   @Test
    void testAddPerson() {
        int expectedResult = 39;
        testSupermarket.addPerson();
        assertEquals(expectedResult, testSupermarket.availableCarts);
    }
Java:
    public void addPerson() {
        availableCarts--;
        soundAlarm();
    }
aber wie lässt sich das hier anders lösen als mit einem getter? Auch wenn die Methode public ist muss ich doch auf das Attribut zugreifen und das ist nunmal private. Oder muss man dafür ein ganz neues Attribut aufrufen?
 
L

LimDul

Java:
    Supermarket testSupermarket = new Supermarket("Testshop", 200, 50, 40);
   @Test
    void testAddPerson() {
        int expectedResult = 39;
        testSupermarket.addPerson();
        assertEquals(expectedResult, testSupermarket.availableCarts);
    }
Java:
    public void addPerson() {
        availableCarts--;
        soundAlarm();
    }
aber wie lässt sich das hier anders lösen als mit einem getter? Auch wenn die Methode public ist muss ich doch auf das Attribut zugreifen und das ist nunmal private. Oder muss man dafür ein ganz neues Attribut aufrufen?
Was genau willst du - losgelöst vom Code testen?

a) Das nun nur 19 Carts zur Verfügung stehen?
- Ist das eine öffentliche Information deines Supermarktes, gehört es also zur öffentlichen API das ein User des Supermarkt-Objekts drauf zugreifen kann? Dann sollte es auch einen getter geben, mit dem man das testen kann
- Es ist keine öffentliche Information - dann ist es für den Test nicht relevant welchen Wert es hat. Dann ist der Test nicht sinnvoll, weil er den internen Zustand des Objekts testest
b) Du willst testen, das nur dann eine Person in den Supermarkt kann, wenn ein Cart zur Vefügung steht? Test 1: Dann initialisiere deinen Supermarkt z.B. mit 2 Carts, rufe addPerson 2x auf => Muss gehen. Test 2: Wieder mit 2 Carts, aber addPerson 3x aufrufen => Verhalten bei nicht mehr verfügbaren Carts erwarten
 
N

nikva

Aber das wird ja in diesem Fall, auch wenn es nicht logisch ist, immer gehen und availablecarts wird negativ. Eine Bedingung ist aber denke ich nicht vorgesehen da addPerson laut Aufgabenstellung mit 3 Zeilen zu implementieren ist. Mit Bedingung komme ich aber auf 5
Java:
    public void addPerson() {
        if (availableCarts > 0) {
            availableCarts--;
            soundAlarm();
        } else {
            System.out.println("no carts available");
        }
    }
Vielleicht dann doch einfach mit Getter, oder wäre das gegen die Konventionen wenn ich den nur für den Test nutze?
 
W

White_Fox

Du kannst mittels Reflection in den privaten Eingeweiden eines Objekts herumpopeln, aber normalerweise ist das tabu.

Bedenke auch: Wenn du die Implementierung änderst, dann mußt du den Test mitändern. Das ist aufwändig, und mieser Stil obendrein. Genauso stattet man eine Klasse auch nicht mit Methoden aus die man eigentich nicht braucht, und die nur im Unittest vorkommen.

Wenn du eine setter-Methode hast, und keine direkt passende getter-Methode - dann würde ich einen Test schreiben der einfach die setter-Methode aufruft - und das wars. Auch wenn kein Ergebnis direkt geprüfgt wird, so zeigt der Test immer noch daß die Aktion keine Nullpointerexception wirft oder ähnlicher Unsinn geschieht.
Oder wie jemand anders schrieb: Du verzichtest auf den Test.
 
W

White_Fox

Naja...hänge dich lieber weniger damit auf ob das jetzt eine void-Methode ist oder nicht.

Das große Problem bei Entwicklungsaufgaben ist: es gibt keine vorgefertigte Lösung. Was sinnvoll ist und was nicht, das mußt du selber entscheiden (das kann nicht jeder, selbst ein erfolgreicher Abschluß ist nicht mehr unbedingt ein Beleg das man dieses autonome Denken hat) und ich denke mal, darauf zielt die Aufgabe auch etwas ab.

Auch wenn es vielleicht etwas dämlich klingt: An deiner Stelle würde ich mich mal eine halbe Stunde einfach nur hinsetzen und einfach nur darüber nachdenken, was der Sinn eines Unittest ist. Was will man damit erreichen, und was nicht? Überleg dir Probleme und Lösungen dazu.
Das bereitet einen vielleicht weniger direkt auf eine Prüfung vor, macht einen aber zu einem besseren Entwickler als das stumpfe Lösen von Übungsaufgaben.
 
N

nikva

Oder ich rufe einfach 2 mal die Methode auf
Java:
class SupermarketTest {

    Supermarket testSupermarket = new Supermarket("Testshop", 200, 50, 1);

    @Test
    void testGetPersonCount() {

        testSupermarket.addPerson();
        testSupermarket.addPerson();
    }
so beweise ich ja im Prinzip auch, dass beide Zweige funktionieren und ich muss auch nicht auf das Attribut zugreifen obwohl es trotzdem in der Methode verarbeitet wird.
 
Thema: 

Länge eines Arrays in einem Objekt testen

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben