Skip to main content

Services

Learn

What is a service?​

A service is the part of your application that handles all the business logic. Services are, essentially, the meat of your applications. In Spring Boot, we use the @service annotation to tell Spring that our class is a service.

The methods in your services are primarily called from the controller, but services in the same application are also free to communicate with one another (remember, in the RESTful API world, different application can only communicate with each other through their APIs). In this exercise, we will only be implementing one service, but there's no limit on the number of services you can include. Additionally, our service is going to be rather simple, but in practice, your services will be much larger and more complex.

What is Mockito and what do we use it for?​

Mockito is a mocking framework for our unit tests. Mockito is the process of faking or simulating classes. But why would we do that? Let's look at an example.

Say we wanted to write some tests for a controller to make sure that, from the perspective of a client application, our calls return the right information (we did this in Exercise 5). The client application doesn't care how the information is procure, only that it can make a call to an endpoint and get the right information back. The server-side application is a "black box". So, in our unit tests, we want to emulate that. We don't care how the information about our Students in gathered, but we know what it should look like. You will be adding a service to your application in this exercise that is responsible for retrieving the information about the Student. Back for the purposes of testing the controller, we don't need to know how the service does this. This is where mocking comes into play.

We don't need our controller tests to make calls to an actual instance of our service class, so we simulate one instead. We use the @MockBean annotation to denote an object reference as a mock. For instance, this line lets us pretend that we have an instance of StudentService:

@MockBean 
private StudentService studentService;

Now, let's look at an example unit test:

StudentControllerTest.java
@Test 
public void getStudentByIdTest() {
Integer id = 1;
String name = "John";
Student StudentToReturn = new Student(id, name);

Mockito.when(StudentService.getStudentById(id));

assertThat(Student.getId()).isEqualTo(id);
assertThat(Student.getName()).isEqualTo(name);
}

First, look at the first three lines of the method. Here, we're creating a Student. This is the information we want to have at the end of test.

Now take a look at the highlighted line. This where the magic happens. Let's break it down:

  • The when() method accepts a call to our mocked StudentService and returns when that call is made.
  • theReturn() simple returns the given object when it's called.

This line reads like a sentence: "When this call is made, then return this object."

note

This technique is called stubbing, for those who are interested. We also use something called partial mocking, but the details aren't important here.

In short, this line simple returns StudentToReturn when we call getStudentById(id) in our mocked StudentService. But why is that useful?

By mocking our StudentService, we have removed the need to worry about how getStudentById() works. getStudentById() could be an extremely long and complicated method, ut as long as we know what is should return, we don't have to worry about changes in getStudentById() impacting our tests. All the controller cares about is that it makes a call to getStudentById() and get a Student in return, so that's all we need to test. If a developer breaks getStudentById(), the tests for the controller don't have to be rewritten.

In Summary, Mockito is a very useful, time-saving tool. It has a lot of functionality available-more than we can cover and more than we need in the bootcamp-but visit the Mockito JavaDocs to learn more.


Do​

1. Write StudentService​

For now, we only need one method, getStudentById(), that returns a Student object.

In the src/main/java/com.geteche.students directory, create a new package called service.

In The newly created package, create a new Java class called StudentService.java.

StudentService.java
@Service
public class StudentService {

public Student getStudentById(Integer id) {
...
}

}

2. Update StudentController​

We don't want our business logic in our controller-that violates the basic coding principle of separation of concerns-so we need to make call to our new service class.

Update the StudentController.java class in the controller package.

Add the dependency to the StudentService using the @Autowired annotation. Update the getStudentById() method.

StudentController.java
@Autowired
private StudentService studentService;
...
public Student getStudentById(@PathVariable("StudentId") Integer id) {
...
}

3. Write tests​

Write a new unit test for the getStudentById() method we added in StudentService. Additionally, update the unit test for StudentController to utilize Mockito.

StudentServiceTest​

In the src/test/java/com.geteche.students directory, create a new package called service.

In the newly created package, create a new Java class called StudentServiceTest.java.

StudentServiceTest.java
@SpringBootTest
public class StudentServiceTest {

@Autowired
private StudentService studentService;

@Test
public void getStudentByIdTest() {
...
}
}

StudentControllerTest​

In the src/test/java/com.geteche.students/controller package, update StudentControllerTest.java to mock the StudentService object.

StudentControllerTest.java
...
@Autowired
private StudentController studentController;

@MockBean
private StudentService studentService;

@Test
public void getStudentByIdTest() {
...
}
}

Run your tests as in Controllers.

4. Deploy to nonprod​

Deploy the application to nonprod following the same steps as in Controllers.