Tests in Spring-Boot

ExceptionOfExpectation

Bekanntes Mitglied
Hallo Leute, an sich komme ich mit den Tests gut voran, bis zu diesem Punkt wo es mir kein richtiger Fehler angezeigt wird:
Java:
package com.tutego.date4u.core;

import java.awt.image.BufferedImage;

import java.io.File;
import java.io.IOException;

import java.util.Base64;
import java.util.Optional;

import javax.imageio.ImageIO;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;

import com.tutego.date4u.components.AwtBicubicThumbnail;
import com.tutego.date4u.images.FileSystem;
import com.tutego.date4u.images.PhotoService;

@SpringBootTest(properties = {"spring.shell.interactive.enabled=false"})
@ExtendWith(MockitoExtension.class)
public class CopleTest {
    
    Logger log = LoggerFactory.getLogger(getClass());
    
    @Autowired PhotoService photoService;
    @SpyBean AwtBicubicThumbnail thumbnail;
    @MockBean FileSystem fileSystem;
    

    
    @Test
    public void action(){
        
        Optional<byte[]> byteImage = photoService.download("tree");
        log.info("Der Download war erfolgreich");
        byte [] bytes = fileSystem.load("tree.jpg");
        log.info("Das Bild wurde erfolgreich geladen");
        //byte[] bytes = Files.readAllBytes(Paths.get("C:\\Users\\iRahi\\fs\\" + "tree" + ".jpg"));
        photoService.upload(bytes);
        log.info("Das Bild war erfolgreich gespeichert");
        
    }
    
    

}

Spring-Boot meldet:
Code:
15:36:49.809 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [com.tutego.date4u.core.CopleTest]: CopleTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
15:36:49.961 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration com.tutego.date4u.Date4uAppApplication for test class com.tutego.date4u.core.CopleTest

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.0)

2023-12-07T15:36:50.597+01:00  INFO 9436 --- [           main] com.tutego.date4u.core.CopleTest         : Starting CopleTest using Java 17.0.7 with PID 9436 (started by iRahi in C:\Users\iRahi\eclipse-workspace\Date4uApp)
2023-12-07T15:36:50.602+01:00  INFO 9436 --- [           main] com.tutego.date4u.core.CopleTest         : No active profile set, falling back to 1 default profile: "default"
2023-12-07T15:36:52.982+01:00  WARN 9436 --- [           main] org.jline                                : Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)

Die Methoden von zwei Delegierungsklassen (FileSystem, Thumbnail) funktionieren;
Code:
PhotoService:
        public Optional<byte[]> download(String name){
            try {    return Optional.of(fileSystem.load(name + ".jpg"));}
            catch(UncheckedIOException e) {return Optional.empty();}
        }
        public String upload(byte[] imageBytes) {
            String imageName = UUID.randomUUID().toString();
            fileSystem.store(imageName + ".jpg", imageBytes);
            byte[] thumbnailBytes = thumbnail.thumbnail(imageBytes);
            fileSystem.store(imageName + "-thumb.jpg", thumbnailBytes);
            return imageName;
        }
FileSystem:
        public byte[] load(String filename) {
            try {    return Files.readAllBytes(Paths.get(System.getProperty("user.home")).resolve("fs\\" + filename + ".jpg" )); }
            catch(IOException e) {throw new UncheckedIOException(e);}
        }
        public void store(String filename, byte[] bytes) {
            try {Files.write(Paths.get(System.getProperty("user.home")).resolve("fs\\" + filename + ".jpg" ), bytes);}
            catch(IOException e) {throw new UncheckedIOException(e);}
        }

Leider bekomme ich keine vernünftige Rückmeldung und weiß daher nicht woran es scheitert. Meine Vermutung wäre dass es hier um Mock-Objekte handelt. Das Logging funktioniert an anderen Stellen.
 
Im Augenblick verstehe ich Dein Problem nicht. Du machst in dem Test zwar irgendwelche Dinge, aber Du testest da ja nicht wirklich was. Tests sind immer in 3 Schritten durchzuführen:
1. Vorbereitung - hier bereitest Du alles vor, was Du für den eigentlichen Test brauchst.
2. Aufruf - Du machst, was Du testen willst.
3. Verifikation - Du prüfst, ob das, was Du bei 2. gemacht hast, das erwünschte Ergebnis gebracht hat

Nur du machst da gar nichts in dem Sinne. Daher ist es evtl. sinnvoll, wenn Du einmal genauer beschreibst was Du exakt willst und was du erwartest?
 
Im Augenblick verstehe ich Dein Problem nicht. Du machst in dem Test zwar irgendwelche Dinge, aber Du testest da ja nicht wirklich was. Tests sind immer in 3 Schritten durchzuführen:
1. Vorbereitung - hier bereitest Du alles vor, was Du für den eigentlichen Test brauchst.
2. Aufruf - Du machst, was Du testen willst.
3. Verifikation - Du prüfst, ob das, was Du bei 2. gemacht hast, das erwünschte Ergebnis gebracht hat

Nur du machst da gar nichts in dem Sinne. Daher ist es evtl. sinnvoll, wenn Du einmal genauer beschreibst was Du exakt willst und was du erwartest?
Du meinst ich sollte die Methoden einzeln testen? Das würde alles übersichtlicher gestalten. Ich probiere später nochmal und melde mich wieder-
Danke für den Hinweis
 
Du solltest Dir genau überlegen, was Du testen willst. Und dann eben immer genau diese 3 Schritte befolgen.

Und bei Unit-Tests werden kleine Units getestet. Wenn Du also in einer Klasse diese Methode hast:
Java:
        public Optional<byte[]> download(String name){
            try {    return Optional.of(fileSystem.load(name + ".jpg"));}
            catch(UncheckedIOException e) {return Optional.empty();}
        }
dann testest Du wirklich nur diese Methode.

Das bedeutet, dass Du eben fileSystem nicht testen willst. Das bedeutet also: fileSystem muss gemockt werden.

Und dann hättest Du da zwei Durchgänge:
1. Alles ohne Exception.
2. Es tritt eine UncheckedIOException auf.

Diese beiden Pfade kannst Du also Testen. Im ersten Test kannst Du dann so Dinge prüfen a.la.
- Es wird ein Optional mit der Rückgabe von fileSystem.load zurück gegeben.
aber natürlich auch etwas wie:
- fileSystem.load wird genau einmal aufgerufen

Der zweite Pfad ist ähnlich - da prüfst Du halt, dass du ein Optional.empty bekommst, wenn da eine UncheckedIOException geworfen wird.

Und ganz wichtig: Mach in jedem Test am Anfang genau diese Blöcke:
  • prepare (da kannst Du alles initialisieren, das Du so für diesen Test brauchst. Also z.B. das Mocking sicherstellen)
  • execute (Der Aufruf, den du testen willst)
  • assert (Hier kommen dann die Prüfungen. Da solltest Du ein aktuelles JUnit nehmen und dann auch ein assertAll nutzen)

Das mag evtl. am Anfang schwer sein. Aber das sollte so wirklich gemacht werden.

Du kannst aber natürlich auch andere Tests schreiben. Du kannst also z.B. so typische Testreihen aufbauen:
  • Du erzeugst eine Entity, speicherst diese
  • prüfst ob du diese laden kannst und alles korrekt ist
  • dann veränderst Du diese und speicherst und prüfst diese dann erneut nach einem laden
  • dann löschst Du diese und prüfst, dass Du diese dann nicht mehr laden kannst
Das ist auch ein valider Test. Aber das ist dann kein Unit Test mehr! Das wäre dann eher ein Regression Test oder so.

Als Entwickler solltest Du aber üben, Unit Tests zu schreiben. Wenn Du beruflich Software entwickeln willst, dann ist heutzutage meist ein agiles Vorgehen an der Tagesordnung und da übernimmt man dann meist kleine Tasks und die sind dann in der Regel erst erfüllt, wenn der Code incl. (Unit) Tests vorhanden ist. Und da willst Du wirklich nur die Unit bzw. den Bereich, den Du angepasst hast, testen.
 
Du solltest Dir genau überlegen, was Du testen willst. Und dann eben immer genau diese 3 Schritte befolgen.

Und bei Unit-Tests werden kleine Units getestet. Wenn Du also in einer Klasse diese Methode hast:
Java:
        public Optional<byte[]> download(String name){
            try {    return Optional.of(fileSystem.load(name + ".jpg"));}
            catch(UncheckedIOException e) {return Optional.empty();}
        }
dann testest Du wirklich nur diese Methode.

Das bedeutet, dass Du eben fileSystem nicht testen willst. Das bedeutet also: fileSystem muss gemockt werden.

Und dann hättest Du da zwei Durchgänge:
1. Alles ohne Exception.
2. Es tritt eine UncheckedIOException auf.

Diese beiden Pfade kannst Du also Testen. Im ersten Test kannst Du dann so Dinge prüfen a.la.
- Es wird ein Optional mit der Rückgabe von fileSystem.load zurück gegeben.
aber natürlich auch etwas wie:
- fileSystem.load wird genau einmal aufgerufen

Der zweite Pfad ist ähnlich - da prüfst Du halt, dass du ein Optional.empty bekommst, wenn da eine UncheckedIOException geworfen wird.

Und ganz wichtig: Mach in jedem Test am Anfang genau diese Blöcke:
  • prepare (da kannst Du alles initialisieren, das Du so für diesen Test brauchst. Also z.B. das Mocking sicherstellen)
  • execute (Der Aufruf, den du testen willst)
  • assert (Hier kommen dann die Prüfungen. Da solltest Du ein aktuelles JUnit nehmen und dann auch ein assertAll nutzen)

Das mag evtl. am Anfang schwer sein. Aber das sollte so wirklich gemacht werden.

Du kannst aber natürlich auch andere Tests schreiben. Du kannst also z.B. so typische Testreihen aufbauen:
  • Du erzeugst eine Entity, speicherst diese
  • prüfst ob du diese laden kannst und alles korrekt ist
  • dann veränderst Du diese und speicherst und prüfst diese dann erneut nach einem laden
  • dann löschst Du diese und prüfst, dass Du diese dann nicht mehr laden kannst
Das ist auch ein valider Test. Aber das ist dann kein Unit Test mehr! Das wäre dann eher ein Regression Test oder so.

Als Entwickler solltest Du aber üben, Unit Tests zu schreiben. Wenn Du beruflich Software entwickeln willst, dann ist heutzutage meist ein agiles Vorgehen an der Tagesordnung und da übernimmt man dann meist kleine Tasks und die sind dann in der Regel erst erfüllt, wenn der Code incl. (Unit) Tests vorhanden ist. Und da willst Du wirklich nur die Unit bzw. den Bereich, den Du angepasst hast, testen.
Ich hatte einen Hintergedanken das Mock-Objekt besser zu verstehen. So wie ich es verstanden habe ist ein Mock-Objekt eine Art von Proxy, daher wollte ich es testen. Ich merke mir die Vorgehensweise und setze es später um.
 

Zurück
Oben