-
Notifications
You must be signed in to change notification settings - Fork 2
Tag 12: VORMITTAG & Integrationtest
Lombok ist eine Java Bibliothek, die die Möglichkeiten der automatischen Code-Generierung erweitert. Hierfür werden Annotationen genutzt.
-
@Getter&@Setter @ToString@EqualsAndHashCode-
@NoArgsConstructor&@AllArgsConstructor -
@Data(ersetzt alle oben genannten Annotations) -
@Value(ersetzt alle oben genannten Annotations, außer @Setter) -
@Builder(erstellt einen Builder für die Klasse)
private String id;
private String name;
public Product() {
}
public Product(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product product)) return false;
if (getId() != null ? !getId().equals(product.getId()) : product.getId() != null) return false;
return getName() != null ? getName().equals(product.getName()) : product.getName() == null;
}
@Override
public int hashCode() {
int result = getId() != null ? getId().hashCode() : 0;
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Product{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private String id;
private String name;
}
| Vorteile | Nachteile |
|---|---|
| kürzerer Code | externe Library |
| weniger Fehlerquellen | Updates der Library als potentielles Risiko |
| weniger zu testender Code | Code wenig nachvollziehbar für Entwickler, die nicht mit Lombok vertraut sind |
Ein Record ist eine spezielle Klasse, die als "schlanke read-only Datenstruktur" verwendet wird. Auch so kann wieder Code gespart werden. Wird eigentlich nur auf Model- (=Daten-) Klassen angewendet. Attribute sind immer private und final.
Im Record werden automatisch generiert:
-
Getter(es gibt keinen Setter, da immutable!) -
equals()& `hasCode() toString()- --> ein Leer-Konstruktor wird nicht generiert
public record Product(
String id,
String name
) {
}
| Vorteile | Nachteile |
|---|---|
| immutable, also unveränderbar | immutable, also unveränderbar |
| kürzerer Code | kann nicht von anderen Klassen erweitert werden (Inheritance) |
| weniger zu testender Code | Code wenig nachvollziehbar für Entwickler, die nicht mit Records vertraut sind |
| weniger Fehlerquellen | |
| können mit Lombok kombiniert werden |
Integrationstest testet, ob im Projekt alle Abhängigkeiten zusammen als Ganzes funktionieren (Test, der alle drei Schichten - s.u. - durchläuft). Getestet wird im Dreischichtenmodell von oben nach unten:
mockMvC: Objekt, über welches Anfragen an Server versendet werden können (wird neu erstellt via @Autowired)
@DirtiesContext: entweder über die Klasse schreiben, um nach jedem Test neutralen zustand herzustellen, oder: über alle Tests schreiben, welche neutralen Zustand benötigen
// Wir schreiben einen Integrationstest und stellen sicher dass:
// - die HTTP Methoden ausgeführt werden
// - der Service läuft
// - das Repo die Daten hergibt
// @SpringBootTest = Fährt den Spring Kontext hoch (Server, Datenbankverbindung, erzeugt Objekte, etc.)
// @AutoConfigureMockMvc = Konfiguriert MockMvc nach unseren Testbedürfnissen
@SpringBootTest
@AutoConfigureMockMvc
class ShopIntegrationTest {
// Mock = So was wie Nachmachen
// MockMvc ermöglicht uns Anfragen nachzumachen
// Damit mockMvc NICHT null ist, müssen wir die Abhängigkeit, d.h. die DEPENDENCY bereitstellen...
@Autowired
MockMvc mockMvc;
@Autowired
ProductRepository productRepository;
// 1. Wurde die Anfrage verstanden und angenommen? -> Status 200 = OK
// 2. Bekommen wir alle Produkte zurück?
// Wenn die Datenbank leer ist -> bekommen wir eine leere Liste []
// Wenn wir alle Produkte abrufen
// Sollen alle Produkte zurück kommen
// Testmethode + sollte ...
// PS: Es könnte vllllt eine Exception entstehen, das ist uns dann aber egal, weil dann ist der Test eben fehlgeschlagen
@Test
void getAllProducts_shouldReturnEmptyList_whenRepositoryIsEmpty() throws Exception {
// mockMvc.perform -> wie bei Postman, verschickt eine Anfrage
// GET = HTTP Verb um etwas anzufragen
mockMvc.perform(get("/api/shop/products"))
.andExpect(status().isOk())
.andExpect(content().json(
"""
[]
"""));
}
@Test
// @DirtiesContext = Putzkraft => Wenn du etwas eingefügt hast ins Repo, lösche es danach wieder!
// Immer über den Methoden, die den Speicher verändern
@DirtiesContext
void getAllProducts_shouldReturnListWithOneProduct_whenRepositoryHasOneProduct() throws Exception {
// Wie kriegen wir etwas ins Repo?
// zB per POST .. oder?
// einfach direkt mit einem Repositoryaufruf!
Product product = new Product("Georgischer Rotwein", "1");
productRepository.add(product);
// mockMvc.perform -> wie bei Postman, verschickt eine Anfrage
// GET = HTTP Verb um etwas anzufragen
mockMvc.perform(get("/api/shop/products"))
.andExpect(status().isOk())
.andExpect(content().json(
"""
[
{
"name": "Georgischer Rotwein",
"id": "1"
}
]
"""));
}
// Wenn wir ein Produkt an den Controller senden, wird es erstellt.
// Dieses neu erstellte Produkt erwarten wir als Antwort
@Test
@DirtiesContext
void addProduct_shouldReturnCreatedProduct() throws Exception {
// Wir verschicken eine POST Anfrage an die URL
mockMvc.perform(
// WAS WIR VERSCHICKEN
post("/api/shop/products")
// Das ist unser Format - wir verschicken fast immer JSON
.contentType(MediaType.APPLICATION_JSON)
// Was im Body beim POST-Request verschickt wird
.content(
"""
{
"name": "Primitivo Wein",
"id": "1"
}
"""
))
// VERGLEICH - IST UNSER ERGEBNIS RICHTIG?
.andExpect(
// Der Status den wir zurück bekommen
status().isOk()
)
.andExpect(
// Das ist der Request Body - Der Inhalt den wir bekommen
content().json(
"""
{
"name": "Primitivo Wein",
"id": "1"
}
"""
));
}
}
Beim Coach gab es Importprobleme, einen Import hatte er manuell hinzugefügt (ich meine es war: MockMvcRequestBuilders.get), hierfür nochmal alle Imports untenstehend falls das hier auch bei jemandem vorkommt:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.junit.jupiter.api.Assertions.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;)