diff --git a/pom.xml b/pom.xml
index 2c23e76..60941b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,30 +24,38 @@
11
-
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
-
- org.postgresql
- postgresql
- runtime
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
@@ -58,5 +66,4 @@
-
diff --git a/src/test/java/com/example/postgresdemo/controller/QuestionControllerTest.java b/src/test/java/com/example/postgresdemo/controller/QuestionControllerTest.java
new file mode 100644
index 0000000..15229c4
--- /dev/null
+++ b/src/test/java/com/example/postgresdemo/controller/QuestionControllerTest.java
@@ -0,0 +1,183 @@
+package com.example.postgresdemo.controller;
+
+import com.example.postgresdemo.model.Question;
+import com.example.postgresdemo.repository.QuestionRepository;
+
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+
+import java.nio.CharBuffer;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest
+
+@AutoConfigureMockMvc
+public class QuestionControllerTest {
+ @Autowired
+ private QuestionRepository questionRepository;
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @AfterEach
+ void deleteQuestions() {
+ questionRepository.deleteAll();
+ }
+
+ @Test
+ void testGetQuestionsWithAmountLessThanPageSize() throws Exception {
+ int assertionNumber = 10;
+ int pageSize = 20;
+
+ fillQuestions(assertionNumber);
+
+ mockMvc.perform(get("/questions")
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.content.length()", Matchers.equalTo(assertionNumber)))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.totalElements", Matchers.equalTo(assertionNumber)))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.totalPages", Matchers.equalTo(1)));
+ }
+
+ @Test
+ void testGetQuestionsWithAmountMoreThanPageSize() throws Exception {
+ int assertionNumber = 30;
+ int pageSize = 20;
+ int totalPages = (int) Math.ceil(assertionNumber / (double) pageSize);
+
+ fillQuestions(assertionNumber);
+
+ mockMvc.perform(get("/questions")
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.content.length()", Matchers.equalTo(pageSize)))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.totalElements", Matchers.equalTo(assertionNumber)))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.totalPages", Matchers.equalTo(totalPages)));
+ }
+
+ @Test
+ void testCreateCorrectQuestion() throws Exception {
+ mockMvc.perform(MockMvcRequestBuilders.post("/questions")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\n" +
+ " \"title\": \"Question 1\",\n" +
+ " \"description\": \"Description 1\"\n" +
+ "}"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.title", Matchers.equalTo("Question 1")))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.description", Matchers.equalTo("Description 1")));
+ }
+
+ @Test
+ void testCreateQuestionWithoutTitle() throws Exception {
+ mockMvc.perform(MockMvcRequestBuilders.post("/questions")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\n" +
+ " \"description\": \"Description\"\n" +
+ "}"))
+ .andExpect(status().is4xxClientError());
+ }
+
+ @Test
+ void testCreateQuestionWithTitleLesThenThreeChars() throws Exception {
+ mockMvc.perform(MockMvcRequestBuilders.post("/questions")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\n" +
+ " \"title\": \"Te\",\n" +
+ " \"description\": \"Description\"\n" +
+ "}"))
+ .andExpect(status().is4xxClientError());
+ }
+
+ @Test
+ void testCreateQuestionWithTitleMoreThenHundredChars() throws Exception {
+ int numberOfChars = 101;
+ String title = CharBuffer.allocate(numberOfChars).toString().replace('\0', 'T');
+
+ mockMvc.perform(MockMvcRequestBuilders.post("/questions")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\n" +
+ " \"title\": \"" + title + "\",\n" +
+ " \"description\": \"Description\"\n" +
+ "}"))
+ .andExpect(status().is4xxClientError());
+ }
+
+ @Test
+ void testCreateQuestionWithoutDescription() throws Exception {
+ mockMvc.perform(MockMvcRequestBuilders.post("/questions")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\n" +
+ " \"title\": \"Question 1\"\n" +
+ "}"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.title", Matchers.equalTo("Question 1")))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.description", Matchers.equalTo(null)));
+ }
+
+ @Test
+ void testUpdateQuestion() throws Exception {
+ fillQuestions(1);
+ long questionId = questionRepository.findAll().get(0).getId();
+
+ mockMvc.perform(MockMvcRequestBuilders.put("/questions/" + questionId)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\n" +
+ " \"title\": \"Edited Question 1\",\n" +
+ " \"description\": \"Edited Description 1\"\n" +
+ "}"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.title", Matchers.equalTo("Edited Question 1")))
+ .andExpect(MockMvcResultMatchers.jsonPath("$.description", Matchers.equalTo("Edited Description 1")));
+ }
+
+ @Test
+ void testUpdateQuestionWithNonExistingId() throws Exception {
+ fillQuestions(1);
+ long questionId = questionRepository.findAll().get(0).getId();
+
+ mockMvc.perform(MockMvcRequestBuilders.put("/questions/" + (questionId + 1))
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("{\n" +
+ " \"title\": \"Edited Question 1\",\n" +
+ " \"description\": \"Edited Description 1\"\n" +
+ "}"))
+ .andExpect(status().is4xxClientError());
+ }
+
+ @Test
+ void testDeleteQuestion() throws Exception {
+ fillQuestions(1);
+ long questionId = questionRepository.findAll().get(0).getId();
+
+ mockMvc.perform(MockMvcRequestBuilders.delete("/questions/" + questionId)
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk());
+ }
+
+ private void fillQuestions(Integer number) {
+ for (int i = 0; i < number; i++) {
+ Question question = new Question();
+ question.setTitle("Question " + i);
+ question.setDescription("Description " + i);
+ questionRepository.save(question);
+ }
+ }
+}
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
new file mode 100644
index 0000000..6d94c03
--- /dev/null
+++ b/src/test/resources/application.properties
@@ -0,0 +1,14 @@
+spring.datasource.url=jdbc:h2:mem:test;MODE=PostgreSQL;
+spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.username=${DB_USER}
+spring.datasource.password=${DB_PASSWORD}
+# We add the MySQL Dialect so that it understands and generates the query based on MySQL
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
+
+spring.h2.console.enabled=true
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.properties.hibernate.format_sql=true
+#spring.jpa.properties.hibernate.show_sql=true
+
+
+spring.sql.init.mode=always