Testen im Spring Boot

Testen im Frühjahr Boot

1. Überblick

In diesem Artikel sehen wir unswriting tests using the framework support in Spring Boot an. Wir werden Unit-Tests behandeln, die isoliert ausgeführt werden können, sowie Integrationstests, die den Spring-Kontext booten, bevor Tests ausgeführt werden.

Wenn Sie Spring Boot noch nicht kennen, lesen Sie unsereintro to Spring Boot.

Weitere Lektüre:

Entdecken Sie die Spring Boot TestRestTemplate

Erfahren Sie, wie Sie mit dem neuen TestRestTemplate in Spring Boot eine einfache API testen.

Read more

Kurzanleitung zu @RestClientTest in Spring Boot

Eine schnelle und praktische Anleitung zur Anmerkung @RestClientTest in Spring Boot

Read more

Injizieren von Mockito Mocks in Frühlingsbohnen

Dieser Artikel zeigt, wie Sie mithilfe der Abhängigkeitsinjektion Mockito-Mocks in Spring Beans für Komponententests einfügen können.

Read more

2. Projektaufbau

Die Anwendung, die wir in diesem Artikel verwenden werden, ist eine API, die einige grundlegende Operationen für dieEmployee-Ressource bereitstellt. Dies ist eine typische abgestufte Architektur. Der API-Aufruf wird vonController überService bis zur SchichtPersistenceverarbeitet.

3. Maven-Abhängigkeiten

Fügen wir zunächst unsere Testabhängigkeiten hinzu:


    org.springframework.boot
    spring-boot-starter-test
    test
    2.1.6.RELEASE


    com.h2database
    h2
    test
    1.4.194

spring-boot-starter-test ist die primäre Abhängigkeit, die die meisten für unsere Tests erforderlichen Elemente enthält.

DasH2 DB ist unsere In-Memory-Datenbank. Es ist nicht mehr erforderlich, eine Datenbank zu Testzwecken zu konfigurieren und zu starten.

4. Integrationstests mit@DataJpaTest

Wir werden mit einer Entität namensEmployee arbeiten, deren Eigenschaftenid undname sind:

@Entity
@Table(name = "person")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Size(min = 3, max = 20)
    private String name;

    // standard getters and setters, constructors
}

Und hier ist unser Repository - mit Spring Data JPA:

@Repository
public interface EmployeeRepository extends JpaRepository {

    public Employee findByName(String name);

}

Das war's für den Persistenzschichtcode. Gehen wir nun zum Schreiben unserer Testklasse.

Lassen Sie uns zunächst das Skelett unserer Testklasse erstellen:

@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryIntegrationTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private EmployeeRepository employeeRepository;

    // write test cases here

}

@RunWith(SpringRunner.class) wird verwendet, um eine Brücke zwischen Spring Boot-Testfunktionen und JUnit zu schlagen. Wenn wir in unseren JUnit-Tests Spring Boot-Testfunktionen verwenden, ist diese Anmerkung erforderlich.

@DataJpaTest bietet einige Standardeinstellungen, die zum Testen der Persistenzschicht erforderlich sind:

  • Konfigurieren von H2, einer In-Memory-Datenbank

  • Einstellen von Ruhezustand, Federdaten undDataSource

  • Durchführen eines@EntityScan

  • Aktivieren Sie die SQL-Protokollierung

Um einen DB-Vorgang auszuführen, müssen einige Datensätze bereits in unserer Datenbank eingerichtet sein. Um diese Daten einzurichten, können wirTestEntityManager.The TestEntityManager provided by Spring Boot is an alternative to the standard JPA EntityManager that provides methods commonly used when writing tests. verwenden

EmployeeRepository ist die Komponente, die wir testen werden. Schreiben wir nun unseren ersten Testfall:

@Test
public void whenFindByName_thenReturnEmployee() {
    // given
    Employee alex = new Employee("alex");
    entityManager.persist(alex);
    entityManager.flush();

    // when
    Employee found = employeeRepository.findByName(alex.getName());

    // then
    assertThat(found.getName())
      .isEqualTo(alex.getName());
}

Im obigen Test verwenden wirTestEntityManager, umEmployee in die Datenbank einzufügen und über die API zum Suchen nach Namen zu lesen.

Der TeilassertThat(…) stammt vonAssertj library, der im Lieferumfang von Spring Boot enthalten ist.

5. Verspotten mit@MockBean

UnserService Layer-Code hängt von unseremRepository ab. Um dieService-Schicht zu testen, müssen wir jedoch nicht wissen oder uns darum kümmern, wie die Persistenzschicht implementiert ist:

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Override
    public Employee getEmployeeByName(String name) {
        return employeeRepository.findByName(name);
    }
}

Im Idealfall sollten wir in der Lage sein, unseren Service-Layer-Code zu schreiben und zu testen, ohne die gesamte Persistenzschicht zu verdrahten.

Um dies zu erreichen,we can use the mocking support provided by Spring Boot Test.

Schauen wir uns zuerst das Skelett der Testklasse an:

@RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {

    @TestConfiguration
    static class EmployeeServiceImplTestContextConfiguration {

        @Bean
        public EmployeeService employeeService() {
            return new EmployeeServiceImpl();
        }
    }

    @Autowired
    private EmployeeService employeeService;

    @MockBean
    private EmployeeRepository employeeRepository;

    // write test cases here
}

Um die KlasseService zu überprüfen, muss eine Instanz der KlasseServiceerstellt und als@Bean verfügbar sein, damit wir sie in unserer Testklasse@Autowire können. Diese Konfiguration wird mithilfe der Annotation@TestConfigurationerreicht.

Beim Scannen von Komponenten werden möglicherweise Komponenten oder Konfigurationen, die nur für bestimmte Tests erstellt wurden, versehentlich überall abgerufen. Um dies zu verhindern,Spring Boot provides @TestConfiguration annotation that can be used on classes in src/test/java to indicate that they should not be picked up by scanning.

Eine weitere interessante Sache ist die Verwendung von@MockBean. Es istcreates a Mock fürEmployeeRepository, mit denen der Aufruf an die tatsächlichenEmployeeRepository umgangen werden kann:

@Before
public void setUp() {
    Employee alex = new Employee("alex");

    Mockito.when(employeeRepository.findByName(alex.getName()))
      .thenReturn(alex);
}

Da die Einrichtung abgeschlossen ist, wird der Testfall einfacher:

@Test
public void whenValidName_thenEmployeeShouldBeFound() {
    String name = "alex";
    Employee found = employeeService.getEmployeeByName(name);

     assertThat(found.getName())
      .isEqualTo(name);
 }

6. Unit Testing mit@WebMvcTest

UnsereController hängen von derService-Schicht ab; Lassen Sie uns der Einfachheit halber nur eine einzige Methode einschließen:

@RestController
@RequestMapping("/api")
public class EmployeeRestController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/employees")
    public List getAllEmployees() {
        return employeeService.getAllEmployees();
    }
}

Da wir uns nur auf denController-Code konzentrieren, ist es natürlich, denService-Schichtencode für unsere Komponententests zu verspotten:

@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeRestControllerIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private EmployeeService service;

    // write test cases here
}

Um dieControllers zu testen, können wir@WebMvcTest verwenden. Die Spring MVC-Infrastruktur wird für unsere Komponententests automatisch konfiguriert.

In den meisten Fällen ist @WebMvcTest darauf beschränkt, einen einzelnen Controller zu booten. Es wird zusammen mit@MockBean verwendet, um Scheinimplementierungen für erforderliche Abhängigkeiten bereitzustellen.

@WebMvcTest konfiguriert auchMockMvc automatisch, was eine leistungsstarke Möglichkeit bietet, MVC-Controller einfach zu testen, ohne einen vollständigen HTTP-Server zu starten.

Nachdem dies gesagt ist, schreiben wir unseren Testfall:

@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
  throws Exception {

    Employee alex = new Employee("alex");

    List allEmployees = Arrays.asList(alex);

    given(service.getAllEmployees()).willReturn(allEmployees);

    mvc.perform(get("/api/employees")
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(jsonPath("$", hasSize(1)))
      .andExpect(jsonPath("$[0].name", is(alex.getName())));
}

Der Methodenaufrufget(…) kann durch andere Methoden ersetzt werden, die HTTP-Verben wieput(),post() usw. entsprechen. Bitte beachten Sie, dass wir auch den Inhaltstyp in der Anfrage festlegen.

MockMvc ist flexibel und wir können damit jede Anfrage erstellen.

7. Integrationstests mit@SpringBootTest

Wie der Name schon sagt, konzentrieren sich Integrationstests auf die Integration verschiedener Schichten der Anwendung. Das heißt auch, dass es sich nicht um Spott handelt.

Ideally, we should keep the integration tests separated from the unit tests and should not run along with the unit tests. Wir können dies tun, indem wir ein anderes Profil verwenden, um nur die Integrationstests auszuführen. Einige Gründe dafür könnten sein, dass die Integrationstests zeitaufwändig sind und möglicherweise eine Datenbank zur Ausführung benötigen.

In diesem Artikel konzentrieren wir uns jedoch nicht darauf und verwenden stattdessen den speicherinternen H2-Persistenzspeicher.

Die Integrationstests müssen einen Container starten, um die Testfälle auszuführen. Daher ist hierfür ein zusätzliches Setup erforderlich - all dies ist in Spring Boot einfach:

@RunWith(SpringRunner.class)
@SpringBootTest(
  SpringBootTest.WebEnvironment.MOCK,
  classes = Application.class)
@AutoConfigureMockMvc
@TestPropertySource(
  locations = "classpath:application-integrationtest.properties")
public class EmployeeRestControllerIntegrationTest {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private EmployeeRepository repository;

    // write test cases here
}

Die Annotation@SpringBootTest kann verwendet werden, wenn der gesamte Container gebootet werden muss. Die Annotation erstellt dieApplicationContext, die in unseren Tests verwendet werden.

Wir können daswebEnvironment-Attribut von@SpringBootTest verwenden, um unsere Laufzeitumgebung zu konfigurieren. Wir verwenden hierWebEnvironment.MOCK, damit der Container in einer Schein-Servlet-Umgebung betrieben wird.

Wir können die Annotation@TestPropertySourceverwenden, um Speicherorte von Eigenschaftendateien zu konfigurieren, die für unsere Tests spezifisch sind. Bitte beachten Sie, dass die mit@TestPropertySource geladene Eigenschaftendatei die vorhandeneapplication.properties-Datei überschreibt.

application-integrationtest.properties enthält die Details zum Konfigurieren des Persistenzspeichers:

spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect

Wenn wir unsere Integrationstests gegen MySQL ausführen möchten, können wir die obigen Werte in der Eigenschaftendatei ändern.

Die Testfälle für die Integrationstests sehen möglicherweise ähnlich aus wie dieController-Schichteinheitentests:

@Test
public void givenEmployees_whenGetEmployees_thenStatus200()
  throws Exception {

    createTestEmployee("bob");

    mvc.perform(get("/api/employees")
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isOk())
      .andExpect(content()
      .contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
      .andExpect(jsonPath("$[0].name", is("bob")));
}

Der Unterschied zu denController-Schicht-Unit-Tests besteht darin, dass hier nichts verspottet wird und End-to-End-Szenarien ausgeführt werden.

8. Automatisch konfigurierte Tests

Eine der erstaunlichen Funktionen der automatisch konfigurierten Anmerkungen von Spring Boot besteht darin, dass Teile der gesamten Anwendung geladen und bestimmte Ebenen der Codebasis getestet werden können.

Zusätzlich zu den oben genannten Anmerkungen finden Sie hier eine Liste einiger häufig verwendeter Anmerkungen:

  • @WebFluxTest– we kann die Annotation@WebFluxTest verwenden, um Spring Webflux-Controller zu testen. Es wird häufig zusammen mit@MockBean verwendet, um Scheinimplementierungen für erforderliche Abhängigkeiten bereitzustellen.

  • @JdbcTest – we kann die Annotation@JdbcTest zum Testen von JPA-Anwendungen verwenden, dies gilt jedoch für Tests, für die nurDataSource. erforderlich sind. Die Annotation konfiguriert eine im Speicher eingebettete Datenbank undJdbcTemplate.

  • @JooqTest – Um jOOQ-bezogene Tests zu testen, können wir die Annotation@JooqTestverwenden, mit der ein DSLContext konfiguriert wird.

  • @DataMongoTest – Um MongoDB-Anwendungen zu testen@DataMongoTest ist eine nützliche Anmerkung. Standardmäßig konfiguriert es eine im Speicher eingebettete MongoDB, wenn der Treiber über Abhängigkeiten verfügbar ist, konfiguriert einenMongoTemplate,-Scan nach@Document-Klassen und konfiguriert Spring Data MongoDB-Repositorys.

  • @DataRedisTest – makes it easier to testRedis Anwendungen. Es sucht nach@RedisHash-Klassen und konfiguriert standardmäßig Spring Data Redis-Repositorys.

  • @DataLdapTest – konfiguriert ein in den Speicher eingebettetesLDAP (falls verfügbar), konfiguriert einLdapTemplate, sucht nach@Entry Klassen und konfiguriert standardmäßig Spring DataLDAP Repositorys

  • @RestClientTest – we verwendet im Allgemeinen die Annotation@RestClientTest, um REST-Clients zu testen. Es konfiguriert automatisch verschiedene Abhängigkeiten wie die Unterstützung von Jackson, GSON und Jsonb, konfiguriert einRestTemplateBuilder und fügt standardmäßig Unterstützung fürMockRestServiceServer hinzu.

9. Fazit

In diesem Tutorial haben wir uns eingehend mit der Testunterstützung in Spring Boot befasst und gezeigt, wie Unit-Tests effizient geschrieben werden können.

Der vollständige Quellcode dieses Artikels kannfound over on GitHub sein. Der Quellcode enthält viele weitere Beispiele und verschiedene Testfälle.

Und wenn Sie mehr über das Testen erfahren möchten, haben wir separate Artikel zuintegration tests undunit tests in JUnit 5.