$30 off During Our Annual Pro Sale. View Details »

Testing the Spring Framework

Testing the Spring Framework

Guest lecture to first year CS students at TU Delft.

Stéphane Nicoll

June 20, 2019
Tweet

More Decks by Stéphane Nicoll

Other Decks in Technology

Transcript

  1. © Copyright 2019 Pivotal Software, Inc. All rights Reserved.
    Testing the Spring Framework
    Stéphane Nicoll - @snicoll

    View Slide

  2. Who am I?
    ● Software Engineer at Pivotal (Spring Boot, Spring
    Framework and start.spring.io)
    ● Involved in open source for 15 years (Apache Maven,
    Spring Framework)
    ● snicoll on the Web (Twitter, Github) - [email protected]
    ● Based in Liège, Belgium (come over, we have waffles)

    View Slide

  3. Who am I?
    ● Software Engineer at Pivotal (Spring Boot, Spring
    Framework and start.spring.io)
    ● Involved in open source for 15 years (Apache Maven,
    Spring Framework)
    ● snicoll on the Web (Twitter, Github) - [email protected]
    ● Based in Liège, Belgium (come over, we have waffles)

    View Slide

  4. Prerequisite

    View Slide

  5. Prerequisite
    “They have a good notion of Java and OOP, know how to use IntelliJ,
    are used to building tools such as Maven and Gradle, and know JUnit
    5 very well.
    Maurício Aniche

    View Slide

  6. Prerequisite
    “They have a good notion of Java and OOP, know how to use IntelliJ,
    are used to building tools such as Maven and Gradle, and know JUnit
    5 very well.
    Maurício Aniche

    View Slide

  7. This is what I was doing during my 1st year

    View Slide

  8. This is what I was doing during my 1st year

    View Slide

  9. Once upon a time…

    View Slide

  10. We stored libs in source control

    View Slide

  11. We stored libs in source control

    View Slide

  12. The power of convention over configuration

    View Slide

  13. And we had to configure the infrastructure ourselves

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/data/jpa
    https://www.springframework.org/schema/data/jpa/spring-jpa.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">



    class="org.springframework.jdbc.datasource.DriverManagerDataSource">











    ${hibernate.dialect}
    ${hibernate.hbm2ddl.auto}
    ${hibernate.show_sql}
    ${hibernate.format_sql}









    View Slide

  14. And we had to configure the infrastructure ourselves

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/data/jpa
    https://www.springframework.org/schema/data/jpa/spring-jpa.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">



    class="org.springframework.jdbc.datasource.DriverManagerDataSource">











    ${hibernate.dialect}
    ${hibernate.hbm2ddl.auto}
    ${hibernate.show_sql}
    ${hibernate.format_sql}










    View Slide

  15. And chose which libraries to use (and their versions!)

    org.springframework
    spring-context
    ${spring.version}


    org.springframework
    spring-orm
    ${spring.version}


    org.springframework.data
    spring-data-jpa
    1.3.0.RELEASE

    View Slide

  16. And chose which libraries to use (and their versions!)

    org.hibernate.javax.persistence
    hibernate-jpa-2.0-api
    1.0.1.Final


    org.hibernate
    hibernate-entitymanager
    4.1.9.Final
    runtime


    mysql
    mysql-connector-java
    5.1.6
    runtime

    View Slide

  17. And chose which libraries to use (and their versions!)

    org.hibernate.javax.persistence
    hibernate-jpa-2.0-api
    1.0.1.Final


    org.hibernate
    hibernate-entitymanager
    4.1.9.Final
    runtime


    mysql
    mysql-connector-java
    5.1.6
    runtime


    View Slide

  18. The power of convention over configuration

    View Slide

  19. Live Coding
    Enough with the slides already!

    View Slide

  20. Summary
    ● Conventions can significantly improve your productivity
    ● Best practices for teams and the ecosystem at large
    ● Provide sane defaults when you don’t or shouldn’t care
    ● Make upgrade and maintenance easier
    ● …. but shouldn’t get in your way
    ● Built on top of an API you could use to implement something slightly or totally
    different
    ● Can be customised without having you to redefine everything
    ● Depending on the scope, not everything can be the target of a convention

    View Slide

  21. See you in 15
    Break

    View Slide

  22. Why bother testing at all?

    View Slide

  23. Testing to reduce risk
    Risk
    None
    YOLO
    Tests
    0 Lots

    View Slide

  24. Demo
    CI/CD in action on start.spring.io

    View Slide

  25. Unit Tests

    View Slide

  26. Unit tests should not require any “framework”
    @Service
    public class SubmissionService {
    @Autowired
    private SubmissionRepository submissions;
    @Transactional
    public Submission create(SubmissionRequest request) {
    Submission submission = new Submission();
    ...
    return this.submissions.save(submission);
    }
    }

    View Slide

  27. Unit tests should not require any “framework”
    @Service
    public class SubmissionService {
    private final SubmissionRepository submissions;
    public SubmissionService(SubmissionRepository submissions) {
    this.submissions = submissions;
    }
    @Transactional
    public Submission create(SubmissionRequest request) {
    Submission submission = new Submission();
    ...
    return this.submissions.save(submission);
    }
    }

    View Slide

  28. Unit testing a client for an HTTP API
    class SpringBootMetadataReaderTests {
    private final RestTemplate restTemplate = new RestTemplate();
    private final MockRestServiceServer server = MockRestServiceServer.bindTo(
    this.restTemplate).build();
    @Test
    void readAvailableVersions() throws IOException {
    this.server.expect(requestTo("https://spring.io/project_metadata/spring-boot"))
    .andRespond(withSuccess(new ClassPathResource(
    "metadata/sagan/spring-boot.json"), MediaType.APPLICATION_JSON));
    ...
    this.server.verify();
    }
    }

    View Slide

  29. Integration Tests

    View Slide

  30. @SpringBootTest

    View Slide

  31. View Slide

  32. @JsonTest
    @WebMvcTest
    @WebFluxTest
    @DataJpaTest
    @JdbcTest
    @JooqTest
    @DataMongoTest
    @DataNeo4jTest
    @DataRedisTest
    @DataLdapTest
    @RestClientTest

    View Slide

  33. Slice test example
    @RestClientTest(SpringBootMetadataReader.class)
    class SpringBootMetadataReaderTests {
    @Autowired
    private SpringBootMetadataReader springBootMetadataReader;
    @Autowired
    private MockRestServiceServer server;
    @Test
    void readAvailableVersions() throws IOException {
    this.server.expect(requestTo("https://spring.io/project_metadata/spring-boot"))
    .andRespond(withSuccess(new ClassPathResource(
    "metadata/sagan/spring-boot.json"), MediaType.APPLICATION_JSON));
    ...
    this.server.verify();
    }
    }

    View Slide

  34. Slice test example with @MockBean
    @WebMvcTest(controllers = CfpController.class)
    class CfpControllerTest {
    @Autowired
    private MockMvc mvc;
    @MockBean
    private SubmissionService submissionService;
    @Test
    void submitTalk() throws Exception {
    given(this.submissionService.create(any())).willReturn(new Submission());
    this.mvc.perform(post("/submit")
    .param("title", "Alice in Wonderland")
    .param("summary", "my abstract"))
    .andExpect(status().isFound())
    .andExpect(header().string(HttpHeaders.LOCATION,
    "/submit?navSection=submit"));
    verify(this.submissionService).create(any());
    }
    }

    View Slide

  35. https://www.testcontainers.org

    View Slide

  36. testcontainers example
    @DisabledWithoutDockerTestcontainers
    public class ReactiveRestClientAutoConfigurationTests {
    @Container
    static ElasticsearchContainer elasticsearch = new ElasticsearchContainer();
    private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
    .withConfiguration(AutoConfigurations.of(
    ReactiveRestClientAutoConfiguration.class));
    @Test
    void restClientCanQueryElasticsearchNode() {
    String endpoint = elasticsearch.getContainerIpAddress()
    + ":" + elasticsearch.getFirstMappedPort();
    this.contextRunner.withPropertyValues(
    "spring.data.elasticsearch.client.reactive.endpoints=" + endpoint)
    .run((context) -> {
    ReactiveElasticsearchClient client = context.getBean(
    ReactiveElasticsearchClient.class);
    ...
    });
    }
    }

    View Slide

  37. Testing Spring Boot

    View Slide

  38. ApplicationContextRunner
    ● Test utility to prepare a mini ApplicationContext suitable for a very precise
    scenario:
    ● Specific auto-configuration(s)
    ● User configuration, if any
    ● Environment tuning (properties customization)
    ● System properties handling
    ● Starts/Stop the context automatically with a ContextConsumer callback
    ● AssertJ style assert
    ● hasSingleBean, doesNotHaveBean, hasFailed
    ● getBean, getBeans, etc

    View Slide

  39. ApplicationContextRunner example
    @Test
    void shouldFailWhenNativeTypesAreNotAvailable() {
    this.contextRunner
    .run((context) -> {
    assertThat(context).hasFailed();
    assertThat(context.getStartupFailure())
    .hasRootCauseInstanceOf(
    NativeTypesNotAvailableException.class);
    });
    }

    View Slide

  40. ApplicationContextRunner example
    @Test
    void shouldFailWhenNativeTypesAreNotAvailable() {
    this.contextRunner
    .withConfiguration(AutoConfigurations.of(
    Neo4jDataAutoConfiguration.class,
    TransactionAutoConfiguration.class))
    .run((context) -> {
    assertThat(context).hasFailed();
    assertThat(context.getStartupFailure())
    .hasRootCauseInstanceOf(
    NativeTypesNotAvailableException.class);
    });
    }

    View Slide

  41. ApplicationContextRunner example
    @Test
    void shouldFailWhenNativeTypesAreNotAvailable() {
    this.contextRunner
    .withConfiguration(AutoConfigurations.of(
    Neo4jDataAutoConfiguration.class,
    TransactionAutoConfiguration.class))
    .withPropertyValues(
    "spring.data.neo4j.uri=bolt://localhost:7687",
    "spring.data.neo4j.use-native-types:true")
    .run((context) -> {
    assertThat(context).hasFailed();
    assertThat(context.getStartupFailure())
    .hasRootCauseInstanceOf(
    NativeTypesNotAvailableException.class);
    });
    }

    View Slide

  42. ApplicationContextRunner example
    @Test
    void shouldFailWhenNativeTypesAreNotAvailable() {
    this.contextRunner
    .withConfiguration(AutoConfigurations.of(
    Neo4jDataAutoConfiguration.class,
    TransactionAutoConfiguration.class))
    .withPropertyValues(
    "spring.data.neo4j.uri=bolt://localhost:7687",
    "spring.data.neo4j.use-native-types:true")
    .withClassLoader(new FilteredClassLoader(
    "org.neo4j.ogm.drivers.bolt.types"))
    .run((context) -> {
    assertThat(context).hasFailed();
    assertThat(context.getStartupFailure())
    .hasRootCauseInstanceOf(
    NativeTypesNotAvailableException.class);
    });
    }

    View Slide

  43. Classpath tuning
    @RunWith(ModifiedClassPathRunner.class)
    @ClassPathOverrides("javax.servlet:servlet-api:2.5")
    public class NoSuchMethodFailureAnalyzerTests {
    @Test
    public void noSuchMethodErrorIsAnalyzed() {
    Throwable failure = createFailure();
    assertThat(failure).isNotNull();
    FailureAnalysis analysis = new NoSuchMethodFailureAnalyzer()
    .analyze(failure);
    assertThat(analysis).isNotNull();
    ...
    }
    private Throwable createFailure() {
    ...
    }
    }

    View Slide

  44. Links
    ● https://spring.io/projects/spring-boot
    ● https://start.spring.io
    ● https://spring.io/guides
    ● https://concourse-ci.org/
    ● Testing Spring Boot Applications (Andy Wilkinson @ Spring I/O 2019): https://
    www.youtube.com/watch?v=5sjFn9BsAds

    View Slide

  45. © Copyright 2019 Pivotal Software, Inc. All rights Reserved.
    Thank you!
    @snicoll

    View Slide