Starter Project for SpringBoot
This guide will help you understand how Spring Boot Starter Projects enable the key goal of Spring Boot - Quick Start to developing production ready applications.
You will learn
- What features are provided by Spring Boot Starter Projects?
- We will look at an example of Starter Projects
- Get an overview of different starter projects provided by Spring Boot.
Tools you will need
- Maven 3.0+ is your build tool
- Your favorite IDE. We use Eclipse.
- JDK 1.8+
Why do we need Spring Boot Starter Projects?
To understand what starter projects provide, let’s consider an example project without using a starter.
What if we do not have starter projects?
Let’s say we want to develop a web application with Spring MVC.
First of all we would need to identify the frameworks we want to use, which versions of frameworks to use and how to connect them together.
Listed below are some of the dependencies we use in our Spring MVC Course. These include Spring MVC, Jackson Databind (for data binding), Hibernate-Validator (for server side validation using Java Validation API) and Log4j (for logging). When creating this course, we had to choose the compatible versions of all these frameworks.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.0.2.Final</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
We had to add configuration to get all the stuff wired together. Configuration for dispatcher servlet, view resolver, error page, web jars among other configuration.
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<mvc:resources mapping="/webjars/**" location="/webjars/" />
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/todo-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
When using JPA, we would need to similar stuff. We need to add the jars and the configuration for datasource, entity manager, transaction manager etc.
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${db.driver}" />
<property name="jdbcUrl" value="${db.url}" />
<property name="user" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:config/schema.sql" />
<jdbc:script location="classpath:config/data.sql" />
</jdbc:initialize-database>
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="persistenceUnitName" value="hsql_pu" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
Spring Boot Starter Projects
Here’s what the Spring Boot documentations says about starters.
Starters are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, just include the spring-boot-starter-data-jpa dependency in your project, and you are good to go.
Let’s consider an example starter - Spring Boot Starter Web.
If you want to develop a web application or an application to expose restful services, Spring Boot Start Web is the starter to pick. Lets create a quick project with Spring Boot Starter Web using Spring Initializr.
Creating REST Services Application with Spring Initializr
Spring Initializr http://start.spring.io/ is great tool to bootstrap your Spring Boot projects.
As shown in the image above, following steps have to be done.
- Launch Spring Initializr and choose the following
- Choose
com.in28minutes.springbootas Group - Choose
student-servicesas Artifact - Choose following dependencies
- Web
- Choose
- Click Generate Project.
- Import the project into Eclipse.
- If you want to understand all the files that are part of this project, you can go here.
Spring Boot Starter Web
Spring Boot Starter Web brings in 2 important features
- Compatible Dependencies that are needed to develop web applications
- Auto Configuration
Dependency for Spring Boot Starter Web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Dependencies
Following screenshot shows the different dependencies that are added in to our application
Dependencies can be classified into:
- Spring - core, beans, context, aop
- Web MVC - (Spring MVC)
- Jackson - for JSON Binding
- Validation - Hibernate Validator, Validation API
- Embedded Servlet Container - Tomcat
- Logging - logback, slf4j
Any typical web application would use all these dependencies. Spring Boot Starter Web comes pre packaged with these. As a developer, I would not need to worry about either these dependencies or their compatible versions.
Auto Configuration
Spring Boot Starter Web auto configures the basic things that are needed. To understand the features Spring Boot Starter Web brings in, lets run StudentServicesApplication.java as a Java Application and review the log.
Mapping servlet: 'dispatcherServlet' to [/]
Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
Spring Boot Starter Web auto-configures
- Dispatcher Servlet
- Error Page
- Web Jars to manage your static dependencies
- Embedded Servlet Container - Tomcat is the default
The image below shows the different things that might be auto configured by Spring Boot Starter Web
Spring Boot Starter Project Options
As we see from Spring Boot Starter Web, starter projects help us in quickly getting started with developing specific types of applications.
- spring-boot-starter-web-services - SOAP Web Services
- spring-boot-starter-web - Web & RESTful applications
- spring-boot-starter-test - Unit testing and Integration Testing
- spring-boot-starter-jdbc - Traditional JDBC
- spring-boot-starter-hateoas - Add HATEOAS features to your services
- spring-boot-starter-security - Authentication and Authorization using Spring Security
- spring-boot-starter-data-jpa - Spring Data JPA with Hibernate
- spring-boot-starter-cache - Enabling Spring Framework’s caching support
- spring-boot-starter-data-rest - Expose Simple REST Services using Spring Data REST
There are a few starters for technical stuff as well
- spring-boot-starter-actuator - To use advanced features like monitoring & tracing to your application out of the box
- spring-boot-starter-undertow, spring-boot-starter-jetty, spring-boot-starter-tomcat - To pick your specific choice of Embedded Servlet Container
- spring-boot-starter-logging - For Logging using logback
- spring-boot-starter-log4j2 - Logging using Log4j2
Integration testing Rest services in springboot
This guide will help you write great integration tests for your Spring Boot Rest Service. We will use a simple code example creating couple of simple rest services.
You will learn
- What is Integration Testing?
- How to create a Get REST Service for retrieving the courses that a student registered for?
- How to write a integration test for Get REST Service?
- How to create a Post REST Service for registering a course for student?
- How to write a integration test for the POST Service?
Tools you will need
- Maven 3.0+ is your build tool
- Your favorite IDE. We use Eclipse.
- JDK 1.8+
Integration Testing
Following screenshot shows eclipse project with all the files we will create.
We want to create a integration test for
StudentController which is a Rest Controller. StudentController exposes two service methods - one Get and one Post. We will write integration tests for both these service methods.
In the integration test
- We will launch the complete Spring Boot application using
@SpringBootTest - We will invoke the service methods using
TestRestTemplate - We will assert the results using a great JSON assert framework -
org.skyscreamer.jsonassert.JSONAssert
A key part of integration testing is testing all the layers in the application.
Overview
Following is the order we do things in this guide:
- Bootstrap a project using Spring Initializr.
- Implement a Business Service for our API - StudentService.
- Implement the API - using StudentController. First we implement the GET methods and then the POST methods.
- Write Integration Tests for our API.
Creating REST Services Application with Spring Initializr
Spring Initializr http://start.spring.io/ is great tool to bootstrap your Spring Boot projects.
As shown in the image above, following steps have to be done
- Launch Spring Initializr and choose the following
- Choose
com.in28minutes.springbootas Group - Choose
student-servicesas Artifact - Choose following dependencies
- Web
- Actuator
- DevTools
- Choose
- Click Generate Project.
- Import the project into Eclipse.
- If you want to understand all the files that are part of this project, you can go here.
Implementing Business Service for your Application
All applications need data. Instead of talking to a real database, we will use an
ArrayList - kind of an in-memory data store.
A student can take multiple courses. A course has an id, name, description and a list of steps you need to complete to finish the course. A student has an id, name, description and a list of courses he/she is currently registered for. We have StudentService exposing methods to
public List<Student> retrieveAllStudents()- Retrieve details for all studentspublic Student retrieveStudent(String studentId)- Retrieve a specific student detailspublic List<Course> retrieveCourses(String studentId)- Retrieve all courses a student is registered forpublic Course retrieveCourse(String studentId, String courseId)- Retrieve details of a specific course a student is registered forpublic Course addCourse(String studentId, Course course)- Add a course to an existing student
Refer to these files at the bottom of the article for exact implementation of the Service
StudentService and the model classes Course and Student.- src/main/java/com/in28minutes/springboot/model/Course.java
- src/main/java/com/in28minutes/springboot/model/Student.java
- src/main/java/com/in28minutes/springboot/service/StudentService.java
Adding Couple of GET Rest Services
The Rest Service
StudentController exposes couple of get services.@Autowired private StudentService studentService: We are using Spring Autowiring to wire the student service into the StudentController.@GetMapping("/students/{studentId}/courses"): Exposing a Get Service with studentId as a path variable@GetMapping("/students/{studentId}/courses/{courseId}"): Exposing a Get Service for retrieving specific course of a student.@PathVariable String studentId: Value of studentId from the uri will be mapped to this parameter.
package com.in28minutes.springboot.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.in28minutes.springboot.model.Course;
import com.in28minutes.springboot.service.StudentService;
@RestController
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/students/{studentId}/courses")
public List<Course> retrieveCoursesForStudent(@PathVariable String studentId) {
return studentService.retrieveCourses(studentId);
}
@GetMapping("/students/{studentId}/courses/{courseId}")
public Course retrieveDetailsForCourse(@PathVariable String studentId,
@PathVariable String courseId) {
return studentService.retrieveCourse(studentId, courseId);
}
}
Executing the Get Service Using Postman
We will fire a request to http://localhost:8080/students/Student1/courses/Course1 to test the service. Response is as shown below.
{
"id": "Course1",
"name": "Spring",
"description": "10 Steps",
"steps": [
"Learn Maven",
"Import Project",
"First Example",
"Second Example"
]
}
Below picture shows how we can execute this Get Service from Postman - my favorite tool to run rest services. 
Integration Testing the Get Rest Service
When we are writing an integration test for a rest service, we would want to launch the entire spring context.
@SpringBootTest(classes = StudentServicesApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT): Launch the entire Spring Boot Application on a Random Port@LocalServerPort private int port;: Autowire the random port into the variable so that we can use it create the url.createURLWithPort(String uri): Utility method to create the url given an uri. It appends the port.HttpEntity<String> entity = new HttpEntity<String>(null, headers);: We use entity so that we have the flexibility of adding in request headers in future.restTemplate.exchange(createURLWithPort("/students/Student1/courses/Course1"),HttpMethod.GET, entity, String.class): Fire a GET request to the specify uri and get the response as a String.JSONAssert.assertEquals(expected, response.getBody(), false): Assert that the response contains expected fields.
package com.in28minutes.springboot.controller;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import com.in28minutes.springboot.StudentServicesApplication;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StudentServicesApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class StudentControllerIT {
@LocalServerPort
private int port;
TestRestTemplate restTemplate = new TestRestTemplate();
HttpHeaders headers = new HttpHeaders();
@Test
public void testRetrieveStudentCourse() {
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/students/Student1/courses/Course1"),
HttpMethod.GET, entity, String.class);
String expected = "{id:Course1,name:Spring,description:10 Steps}";
JSONAssert.assertEquals(expected, response.getBody(), false);
}
private String createURLWithPort(String uri) {
return "http://localhost:" + port + uri;
}
Adding a POST Rest Service
A POST Service should return a status of created (201) when the resource creation is successful.
@PostMapping("/students/{studentId}/courses"): Mapping a url for the POST Request @RequestBody Course newCourse: Using Binding to bind the body of the request to Course object. ResponseEntity.created(location).build(): Return a status of created. Also return the location of created resource as a Response Header. @PostMapping("/students/{studentId}/courses")
public ResponseEntity<Void> registerStudentForCourse(
@PathVariable String studentId, @RequestBody Course newCourse) {
Course course = studentService.addCourse(studentId, newCourse);
if (course == null)
return ResponseEntity.noContent().build();
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path(
"/{id}").buildAndExpand(course.getId()).toUri();
return ResponseEntity.created(location).build();
}
Executing a POST Rest Service
Example Request is shown below. It contains all the details to register a course to a student.
{
"name": "Microservices",
"description": "10 Steps",
"steps": [
"Learn How to Break Things Up",
"Automate the hell out of everything",
"Have fun"
]
}
Below picture shows how we can execute this Post Service from Postman - my favorite tool to run rest services. Make sure you go to the Body tab and select raw. Select JSON from the dropdown. Copy above request into body.
The URL we use is http://localhost:8080/students/Student1/courses.
Writing Integration Test for the POST Rest Service
When writing an integration test for a POST service we would want to check if the location header contains the uri of the created resource.
String actual = response.getHeaders().get(HttpHeaders.LOCATION).get(0);: Get the Location header from the response.assertTrue(actual.contains("/students/Student1/courses/")): Assert that the header contains the uri of the newly created resource.
@Test
public void addCourse() {
Course course = new Course("Course1", "Spring", "10 Steps",
Arrays.asList("Learn Maven", "Import Project", "First Example",
"Second Example"));
HttpEntity<Course> entity = new HttpEntity<Course>(course, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/students/Student1/courses"),
HttpMethod.POST, entity, String.class);
String actual = response.getHeaders().get(HttpHeaders.LOCATION).get(0);
assertTrue(actual.contains("/students/Student1/courses/"));
}
Next Steps
- Understand more about Unit Testing
- Understand more about **
- Understand more about **
- Understand more about **
- Understand more about **
- Understand more about **
Complete Code Example
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.in28minutes.springboot</groupId>
<artifactId>student-services</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>student-services</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
src/main/java/com/in28minutes/springboot/controller/StudentController.java
package com.in28minutes.springboot.controller;
import java.net.URI;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import com.in28minutes.springboot.model.Course;
import com.in28minutes.springboot.service.StudentService;
@RestController
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/students/{studentId}/courses")
public List<Course> retrieveCoursesForStudent(@PathVariable String studentId) {
return studentService.retrieveCourses(studentId);
}
@GetMapping("/students/{studentId}/courses/{courseId}")
public Course retrieveDetailsForCourse(@PathVariable String studentId,
@PathVariable String courseId) {
return studentService.retrieveCourse(studentId, courseId);
}
@PostMapping("/students/{studentId}/courses")
public ResponseEntity<Void> registerStudentForCourse(
@PathVariable String studentId, @RequestBody Course newCourse) {
Course course = studentService.addCourse(studentId, newCourse);
if (course == null)
return ResponseEntity.noContent().build();
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path(
"/{id}").buildAndExpand(course.getId()).toUri();
return ResponseEntity.created(location).build();
}
}
src/main/java/com/in28minutes/springboot/model/Course.java
package com.in28minutes.springboot.model;
import java.util.List;
public class Course {
private String id;
private String name;
private String description;
private List<String> steps;
// Needed by Caused by: com.fasterxml.jackson.databind.JsonMappingException:
// Can not construct instance of com.in28minutes.springboot.model.Course:
// no suitable constructor found, can not deserialize from Object value
// (missing default constructor or creator, or perhaps need to add/enable
// type information?)
public Course() {
}
public Course(String id, String name, String description, List<String> steps) {
super();
this.id = id;
this.name = name;
this.description = description;
this.steps = steps;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescription() {
return description;
}
public String getName() {
return name;
}
public List<String> getSteps() {
return steps;
}
@Override
public String toString() {
return String.format(
"Course [id=%s, name=%s, description=%s, steps=%s]", id, name,
description, steps);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Course other = (Course) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
src/main/java/com/in28minutes/springboot/model/Student.java
package com.in28minutes.springboot.model;
import java.util.List;
public class Student {
private String id;
private String name;
private String description;
private List<Course> courses;
public Student(String id, String name, String description,
List<Course> courses) {
super();
this.id = id;
this.name = name;
this.description = description;
this.courses = courses;
}
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;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
@Override
public String toString() {
return String.format(
"Student [id=%s, name=%s, description=%s, courses=%s]", id,
name, description, courses);
}
}
src/main/java/com/in28minutes/springboot/service/StudentService.java
package com.in28minutes.springboot.service;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Component;
import com.in28minutes.springboot.model.Course;
import com.in28minutes.springboot.model.Student;
@Component
public class StudentService {
private static List<Student> students = new ArrayList<>();
static {
//Initialize Data
Course course1 = new Course("Course1", "Spring", "10 Steps", Arrays
.asList("Learn Maven", "Import Project", "First Example",
"Second Example"));
Course course2 = new Course("Course2", "Spring MVC", "10 Examples",
Arrays.asList("Learn Maven", "Import Project", "First Example",
"Second Example"));
Course course3 = new Course("Course3", "Spring Boot", "6K Students",
Arrays.asList("Learn Maven", "Learn Spring",
"Learn Spring MVC", "First Example", "Second Example"));
Course course4 = new Course("Course4", "Maven",
"Most popular maven course on internet!", Arrays.asList(
"Pom.xml", "Build Life Cycle", "Parent POM",
"Importing into Eclipse"));
Student ranga = new Student("Student1", "Ranga Karanam",
"Hiker, Programmer and Architect", new ArrayList<>(Arrays
.asList(course1, course2, course3, course4)));
Student satish = new Student("Student2", "Satish T",
"Hiker, Programmer and Architect", new ArrayList<>(Arrays
.asList(course1, course2, course3, course4)));
students.add(ranga);
students.add(satish);
}
public List<Student> retrieveAllStudents() {
return students;
}
public Student retrieveStudent(String studentId) {
for (Student student : students) {
if (student.getId().equals(studentId)) {
return student;
}
}
return null;
}
public List<Course> retrieveCourses(String studentId) {
Student student = retrieveStudent(studentId);
if (student == null) {
return null;
}
return student.getCourses();
}
public Course retrieveCourse(String studentId, String courseId) {
Student student = retrieveStudent(studentId);
if (student == null) {
return null;
}
for (Course course : student.getCourses()) {
if (course.getId().equals(courseId)) {
return course;
}
}
return null;
}
private SecureRandom random = new SecureRandom();
public Course addCourse(String studentId, Course course) {
Student student = retrieveStudent(studentId);
if (student == null) {
return null;
}
String randomId = new BigInteger(130, random).toString(32);
course.setId(randomId);
student.getCourses().add(course);
return course;
}
}
src/main/java/com/in28minutes/springboot/StudentServicesApplication.java
package com.in28minutes.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class StudentServicesApplication {
public static void main(String[] args) {
SpringApplication.run(StudentServicesApplication.class, args);
}
}
src/main/resources/application.properties
src/test/java/com/in28minutes/springboot/controller/StudentControllerIT.java
package com.in28minutes.springboot.controller;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.boot.context.embedded.LocalServerPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import com.in28minutes.springboot.StudentServicesApplication;
import com.in28minutes.springboot.model.Course;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StudentServicesApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class StudentControllerIT {
@LocalServerPort
private int port;
TestRestTemplate restTemplate = new TestRestTemplate();
HttpHeaders headers = new HttpHeaders();
@Test
public void testRetrieveStudentCourse() {
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/students/Student1/courses/Course1"),
HttpMethod.GET, entity, String.class);
String expected = "{id:Course1,name:Spring,description:10 Steps}";
JSONAssert.assertEquals(expected, response.getBody(), false);
}
@Test
public void addCourse() {
Course course = new Course("Course1", "Spring", "10 Steps",
Arrays.asList("Learn Maven", "Import Project", "First Example",
"Second Example"));
HttpEntity<Course> entity = new HttpEntity<Course>(course, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/students/Student1/courses"),
HttpMethod.POST, entity, String.class);
String actual = response.getHeaders().get(HttpHeaders.LOCATION).get(0);
assertTrue(actual.contains("/students/Student1/courses/"));
}
private String createURLWithPort(String uri) {
return "http://localhost:" + port + uri;
}
}