spring-boot
Testen in voorjaarsschoen
Zoeken…
Hoe een eenvoudige Spring Boot-toepassing te testen
We hebben een voorbeeld Spring boot-applicatie die gebruikersgegevens opslaat in MongoDB en we gebruiken Rest-services om gegevens op te halen
Eerst is er een domeinklasse ie POJO
@Document
public class User{
@Id
private String id;
private String name;
}
Een bijbehorende repository op basis van Spring Data MongoDB
public interface UserRepository extends MongoRepository<User, String> {
}
Dan onze gebruikerscontroller
@RestController
class UserController {
@Autowired
private UserRepository repository;
@RequestMapping("/users")
List<User> users() {
return repository.findAll();
}
@RequestMapping(value = "/Users/{id}", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.NO_CONTENT)
void delete(@PathVariable("id") String id) {
repository.delete(id);
}
// more controller methods
}
En tot slot onze Spring Boot Application
@SpringBootApplication
public class Application {
public static void main(String args[]){
SpringApplication.run(Application.class, args);
}
}
Als, laten we zeggen dat John Cena, The Rock en TripleHHH de enige drie gebruikers in de database waren, zou een verzoek aan / gebruikers het volgende antwoord geven:
$ curl localhost:8080/users
[{"name":"John Cena","id":"1"},{"name":"The Rock","id":"2"},{"name":"TripleHHH","id":"3"}]
Om de code te testen, zullen we controleren of de toepassing werkt
@RunWith(SpringJUnit4ClassRunner.class) // 1
@SpringApplicationConfiguration(classes = Application.class) // 2
@WebAppConfiguration // 3
@IntegrationTest("server.port:0") // 4
public class UserControllerTest {
@Autowired // 5
UserRepository repository;
User cena;
User rock;
User tripleHHH;
@Value("${local.server.port}") // 6
int port;
@Before
public void setUp() {
// 7
cena = new User("John Cena");
rock = new User("The Rock");
tripleHHH = new User("TripleHH");
// 8
repository.deleteAll();
repository.save(Arrays.asList(cena, rock, tripleHHH));
// 9
RestAssured.port = port;
}
// 10
@Test
public void testFetchCena() {
String cenaId = cena.getId();
when().
get("/Users/{id}", cenaId).
then().
statusCode(HttpStatus.SC_OK).
body("name", Matchers.is("John Cena")).
body("id", Matchers.is(cenaId));
}
@Test
public void testFetchAll() {
when().
get("/users").
then().
statusCode(HttpStatus.SC_OK).
body("name", Matchers.hasItems("John Cena", "The Rock", "TripleHHH"));
}
@Test
public void testDeletetripleHHH() {
String tripleHHHId = tripleHHH.getId();
when()
.delete("/Users/{id}", tripleHHHId).
then().
statusCode(HttpStatus.SC_NO_CONTENT);
}
}
Uitleg
- Net als elke andere op Spring gebaseerde test hebben we de
SpringJUnit4ClassRunner
zodat een toepassingscontext wordt gemaakt. - De annotatie
@SpringApplicationConfiguration
is vergelijkbaar met de annotatie@ContextConfiguration
omdat deze wordt gebruikt om aan te geven welke toepassingscontext (en) in de test moeten worden gebruikt. Bovendien zal het logica activeren voor het lezen van Spring Boot-specifieke configuraties, eigenschappen, enzovoort. -
@WebAppConfiguration
moet aanwezig zijn om Spring te laten weten dat eenWebApplicationContext
moet worden geladen voor de test. Het biedt ook een kenmerk voor het opgeven van het pad naar de hoofdmap van de webtoepassing. -
@IntegrationTest
wordt gebruikt om Spring Boot te vertellen dat de geïntegreerde webserver moet worden gestart. Door dubbele of gelijk-gescheiden naam-waardeparen te verstrekken, kan elke omgevingsvariabele worden overschreven. In dit voorbeeld overschrijft de"server.port:0"
de standaardpoortinstelling van de server. Normaal gesproken begint de server het opgegeven poortnummer te gebruiken, maar de waarde 0 heeft een speciale betekenis. Wanneer gespecificeerd als 0, vertelt Spring Boot om de poorten in de hostomgeving te scannen en de server op een willekeurige, beschikbare poort te starten. Dat is handig als we verschillende services hebben die verschillende poorten op de ontwikkelmachines en de build-server bezetten die mogelijk in botsing kunnen komen met de applicatiepoort, in welk geval de applicatie niet start. Ten tweede, als we meerdere integratietests maken met verschillende toepassingscontexten, kunnen ze ook botsen als de tests tegelijkertijd worden uitgevoerd. - We hebben toegang tot de toepassingscontext en kunnen autowiring gebruiken om elke springboon te injecteren.
- De
@Value("${local.server.port}”)
wordt omgezet in het daadwerkelijke poortnummer dat wordt gebruikt. - We maken enkele entiteiten die we kunnen gebruiken voor validatie.
- De MongoDB-database wordt gewist en opnieuw geïnitialiseerd voor elke test, zodat we altijd valideren tegen een bekende status. Aangezien de volgorde van de tests niet is gedefinieerd, is de kans groot dat de test testFetchAll () mislukt als deze wordt uitgevoerd na de test testDeletetripleHHH ().
- We geven Rest Assured de juiste poort te gebruiken. Het is een open source-project dat een Java DSL biedt voor het testen van rustgevende services
- Tests worden uitgevoerd met Rest Assured. we kunnen de tests uitvoeren met behulp van de TestRestTemplate of een andere http-client, maar ik gebruik Rest Assured omdat we beknopte documentatie kunnen schrijven met RestDocs
Een ander yaml [of eigenschappen] bestand laden of sommige eigenschappen overschrijven
Wanneer we @SpringApplicationConfiguration
gebruiken, gebruikt het configuratie van application.yml
[eigenschappen] die in bepaalde situaties niet geschikt is. Om de eigenschappen te negeren, kunnen we de annotatie @TestPropertySource
gebruiken.
@TestPropertySource(
properties = {
"spring.jpa.hibernate.ddl-auto=create-drop",
"liquibase.enabled=false"
}
)
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class ApplicationTest{
// ...
}
We kunnen het eigenschappenkenmerk van @TestPropertySource
gebruiken om de specifieke eigenschappen te overschrijven die we willen. In het bovenstaande voorbeeld overschrijven we eigenschap spring.jpa.hibernate.ddl-auto
om create-drop
te create-drop
. En liquibase.enabled
naar false
.
Verschillende YML-bestanden laden
Als u een ander yml- bestand voor de test wilt laden, kunt u het locatiekenmerk gebruiken op @TestPropertySource
.
@TestPropertySource(locations="classpath:test.yml")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class ApplicationTest{
// ...
}
Alternatieve opties
Optie 1:
U kunt ook verschillende yml- bestanden laden door een yml- bestand in test > resource
map test > resource
Optie 2:
Annotatie met @ActiveProfiles
gebruiken
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@ActiveProfiles("somename")
public class MyIntTest{
}
U kunt zien dat we met behulp van @ActiveProfiles
annotatie en we zijn het passeren van de somename als de waarde.
Maak een bestand met de naam application-somename.yml
en de test laadt dit bestand.