Guide SonarQube
Guide SonarQube
Guide SonarQube
Guide : SonarQube
(Installation et Démonstration)
1
Table des Matières
1. Installation........................................................................................................................... 3
2. Interface graphique de SonarQube ...................................................................................... 4
a. Interface de connexion .................................................................................................... 4
b. Création d’un projet sur SonarQube................................................................................ 6
3. Démonstration ................................................................................................................... 10
a. Structure du projet : ....................................................................................................... 10
b. Lancement de l’analyse du projet : ............................................................................... 19
c. Intégration de JACOCO ................................................................................................ 20
2
1. Installation
Pour installer l’image docker de SonarQube il faut suivre la documentation officielle sur le
site : Installation SonarQube image docker
Création des volumes sur docker pour conserver les données
Nous allons travailler avec la base de données H2 intégrée de SonarQube. Seulement en mode
développement. Pour utiliser SonarQube en mode production il faut installer une base de
données soit Microsoft SQL Server, Oracle, PostgreSQL (en anglais) (MySQL n’est plus pris
en charge).
Installation de l’image
docker run --rm \
-p 9000:9000 \
-v sonarqube_extensions:/opt/sonarqube/extensions \
sonarqube
3
Apres l’installation vous pouvez accéder à l’interface sur le port 9000.Par défaut les
identifiants de connexion sont :
Username : admin
Password : admin (Vous devez changer le mot de passe par la suite).
a. Interface de connexion
Voici un exemple d’interface ou vous pouvez voir toutes les informations concernant les
bugs, les vulnérabilités, les mauvaises pratiques de code, la couverture de vos tests apres
analyse de votre code.
4
Avec SonarQube pour que votre code passe il faut qu’il respecte certaines conditions.
Par exemple un code échoue lorsque le nombre de bugs est supérieurs à cinq.
5
b. Création d’un projet sur SonarQube
Sélectionnez le menu projet :
Lors de la création d’un projet choisissez l’option manuel si votre projet que vous voulez
analyser se trouve en local sur votre machine.
6
Ou une autre option si ce n’est pas en local :
Nous allons travailler en local donc il faut choisir l’option manuelle. Et donner un nom à votre
projet.
7
Par la suit il faut choisir de quelle façon l’on veut analyser notre projet dans notre cas nous
allons le faire localement.
Apres il faut générer un token pour le projet avec une durée de minimum 30 jours.
8
Apres la génération du token il faut choisir le scanner qui correspond au projet que l’on veut
analyser. Pour ce projet il faut choisir le scanner pour Maven car le projet est développé avec
Maven.
Une commande sera générée et il suffit de copier et exécuter cette dans commande dans
l’invite de commande dans le dossier de votre projet.
Pour pouvoir exécuter la commande sans problème dans l’invite de commande essayer de
mettre la commande sur une seule ligne.
9
3. Démonstration
Dans cette partie nous allons créer un projet spring boot pour la gestion des étudiants et des
classes. Nous allons analyser ce projet avec SonarQube pour détecter des bugs, des
vulnérabilités, les mauvaises pratiques.
a. Structure du projet :
10
Vous pouvez créer le projet directement dans intellij ou Eclipse ou bien en ligne sur le site de
spring initializr. Le projet utilise une base de données MySQL.
Créer une base de données sur php/MyAdmin et ensuite modifier votre fichier
application.proprties comme suit :
spring.datasource.url=jdbc:mysql://localhost:3306/student_class
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
server.port=8085
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
11
private String Nom;
private String Prenom;
private float age;
@ManyToOne(fetch = FetchType.EAGER)
private Classe classe;
- la classe classe:
- package com.example.student_class.Models;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Classe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nom;
private int grade;
private int nb_module;
Création des interfaces repository pour chaque classe dans le sous package repositories :
- studentRepository :
- package com.example.student_class.Repositories;
import com.example.student_class.Models.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface StudentRepository extends JpaRepository<Student,
Long> {
// You can add custom query methods if needed
}
- classeRepository:
- package com.example.student_class.Repositories;
import com.example.student_class.Models.Classe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
12
@Repository
public interface ClasseRepository extends JpaRepository<Classe, Long>
{
// You can add custom query methods if needed
}
import com.example.student_class.Models.Student;
import com.example.student_class.Repositories.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class StudentService {
private final StudentRepository studentRepository;
@Autowired
public StudentService(StudentRepository studentRepository) {
this.studentRepository = studentRepository;
}
- classeService:
package com.example.student_class.Services;
import com.example.student_class.Models.Classe;
import com.example.student_class.Repositories.ClasseRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
13
@Service
public class ClasseService {
private final ClasseRepository classeRepository;
@Autowired
public ClasseService(ClasseRepository classeRepository) {
this.classeRepository = classeRepository;
}
import com.example.student_class.Models.Student;
import com.example.student_class.Services.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/students")
public class StudentController {
private final StudentService studentService;
@Autowired
public StudentController(StudentService studentService) {
this.studentService = studentService;
}
@GetMapping
public List<Student> getAllStudents() {
return studentService.getAllStudents();
}
@GetMapping("/{id}")
public Student getStudentById(@PathVariable Long id) {
return studentService.getStudentById(id);
}
@PostMapping
14
public Student createStudent(@RequestBody Student student) {
return studentService.createOrUpdateStudent(student);
}
@PutMapping("/{id}")
public Student updateStudent(@PathVariable Long id, @RequestBody
Student student) {
// Set the ID to ensure it's updated for the correct entity
student.setId(id);
return studentService.createOrUpdateStudent(student);
}
@DeleteMapping("/{id}")
public void deleteStudentById(@PathVariable Long id) {
studentService.deleteStudentById(id);
}
}
- classeController:
package com.example.student_class.Controllers;
import com.example.student_class.Models.Classe;
import com.example.student_class.Services.ClasseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/classes")
public class ClasseController {
private final ClasseService classeService;
@Autowired
public ClasseController(ClasseService classeService) {
this.classeService = classeService;
}
@GetMapping
public List<Classe> getAllClasses() {
return classeService.getAllClasses();
}
@GetMapping("/{id}")
public Classe getClassById(@PathVariable Long id) {
return classeService.getClassById(id);
}
@PostMapping
public Classe createClass(@RequestBody Classe classe) {
return classeService.createOrUpdateClass(classe);
}
@PutMapping("/{id}")
public Classe updateClass(@PathVariable Long id, @RequestBody Classe
classe) {
// Set the ID to ensure it's updated for the correct entity
classe.setId(id);
return classeService.createOrUpdateClass(classe);
15
}
@DeleteMapping("/{id}")
public void deleteClassById(@PathVariable Long id) {
classeService.deleteClassById(id);
}
}
Nous allons également créer quelques cas de test car SonarQube évalue un projet en se basant
aussi sur le pourcentage de couverture des tets.
- studentServiceTest
package com.example.student_class;
import com.example.student_class.Models.Student;
import com.example.student_class.Repositories.StudentRepository;
import com.example.student_class.Services.StudentService;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Collections;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
@SpringBootTest
public class StudentServiceTest {
@Mock
private StudentRepository studentRepository;
@InjectMocks
private StudentService studentService;
@Test
public void testGetAllStudents() {
when(studentRepository.findAll()).thenReturn(Collections.emptyList());
assertEquals(0, studentService.getAllStudents().size());
}
@Test
public void testGetStudentById() {
Long id = 1L;
16
Student mockStudent = new Student();
mockStudent.setId(id);
when(studentRepository.findById(id)).thenReturn(Optional.of(mockStudent));
assertEquals(id, retrievedStudent.getId());
}
@Test
public void testCreateOrUpdateStudent() {
Student studentToSave = new Student();
studentToSave.setId(1L);
when(studentRepository.save(studentToSave)).thenReturn(studentToSave);
Student savedStudent =
studentService.createOrUpdateStudent(studentToSave);
assertEquals(studentToSave.getId(), savedStudent.getId());
}
@Test
public void testDeleteStudentById() {
Long id = 1L;
studentService.deleteStudentById(id);
- studentControllerTest :
package com.example.student_class;
import com.example.student_class.Controllers.StudentController;
import com.example.student_class.Models.Student;
import com.example.student_class.Services.StudentService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Collections;
import static
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(StudentController.class)
public class StudentControllerTest {
@Autowired
private MockMvc mockMvc;
17
@MockBean
private StudentService studentService;
@Autowired
private ObjectMapper objectMapper;
@Test
public void testGetAllStudents() throws Exception {
Mockito.when(studentService.getAllStudents()).thenReturn(Collections.emptyL
ist());
mockMvc.perform(get("/api/students"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$").isArray());
}
@Test
public void testGetStudentById() throws Exception {
Long id = 1L;
Student student = new Student();
student.setId(id);
Mockito.when(studentService.getStudentById(id)).thenReturn(student);
mockMvc.perform(get("/api/students/{id}", id))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.id").value(id));
}
@Test
public void testCreateStudent() throws Exception {
Student student = new Student();
student.setId(1L);
mockMvc.perform(post("/api/students")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(student)))
.andExpect(status().isOk());
}
@Test
public void testUpdateStudent() throws Exception {
Long id = 1L;
Student student = new Student();
student.setId(id);
mockMvc.perform(put("/api/students/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(student)))
.andExpect(status().isOk());
}
@Test
public void testDeleteStudentById() throws Exception {
Long id = 1L;
18
mockMvc.perform(delete("/api/students/{id}", id))
.andExpect(status().isOk());
}
}
Apres avoir mis en place le projet, l’exécuter pour s’assurer qu’il n’y a pas d’erreur et que
tout est correctement configuré.
b. Lancement de l’analyse du projet :
Lancer l’invite de commande dans la racine de votre projet et exécuter la commande générer
précédemment avec SonarQube ou générer une nouvelle commande et l’exécuter.
Assurer vous que tout se passe correctement sans erreur surtout au niveau de vos tests.
19
c. Intégration de JACOCO
Actuellement SonarQube ne donne aucune information sur la couverture des tests et c’est
parce qu’il manque un outil ou une dépendance dans le projet qui fournit un rapport sur les
tests.
Ajout de JACOCO :
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<version>0.8.7</version>
<scope>test</scope>
</dependency>
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.ja
coco.reportPath>
<sonar.language>java</sonar.language>
</properties>
20
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
Visualisation
21
22