diff --git a/.gitignore b/.gitignore
index 7b29b8d26bb183b46db6f01f59e6f3f95587410f..36681a2a1181af5fe6c64fd1a4c6a9d51e3ce76a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -385,7 +385,6 @@ Network Trash Folder
 Temporary Items
 .apdisk
 
-=======
 # Local
 .env
 dist
@@ -394,5 +393,8 @@ dist
 .migrate
 src/migrations/data/
 
+# Tests
+junit.xml
+
 # Documentation generated with compodoc
-documentation
\ No newline at end of file
+documentation
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8396a9ed144b0d4185a8bfd0dd085c5ceac7eade..6807e1a45c58a92adc1b16276d1ef780513e9bb5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -61,7 +61,7 @@ test:
     - merge_requests
 
 code_analysis:
-  image: skilldlabs/sonar-scanner:4.0.0
+  image: registry.forge.grandlyon.com/apoyen2/sonnar-scanner-gl:master
   services:
     - docker:18.09-dind
   stage: sonar-analysis
diff --git a/package.json b/package.json
index 1497f941af26c5210397961feb817d3220dfcc59..3a185a1aa134f4dcfd07cf6e7710cc879dfda30d 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
     "release": "standard-version",
     "init-db": "node ./scripts/init-db.js",
     "test": "jest",
-    "test:watch": "jest --watch",
+    "test:watch": "jest --config ./test/jest.json --watch",
     "test:cov": "jest --config ./test/jest.json --coverage --ci --reporters=default --reporters=jest-junit",
     "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
     "test:e2e": "jest --config ./test/jest-e2e.json",
diff --git a/src/admin/admin.controller.spec.ts b/src/admin/admin.controller.spec.ts
index dc0bdef307a30973ed3864fa428e46aca34c8dea..26b7d5b8b9ecdf2aa5acde316dd5abfecda90638 100644
--- a/src/admin/admin.controller.spec.ts
+++ b/src/admin/admin.controller.spec.ts
@@ -12,7 +12,13 @@ import { StructuresSearchService } from '../structures/services/structures-searc
 import { User } from '../users/schemas/user.schema';
 import { UsersService } from '../users/users.service';
 import { AdminController } from './admin.controller';
-import { PendingStructureDto } from './dto/pending-structure.dto';
+import { mockJwtAuthGuard } from '../../test/mock/guards/jwt-auth.mock.guard';
+import { mockRoleGuard } from '../../test/mock/guards/role.mock.guard';
+import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { RolesGuard } from '../users/guards/roles.guard';
+import { UsersServiceMock } from '../../test/mock/services/user.mock.service';
+import { StructuresServiceMock } from '../../test/mock/services/structures.mock.service';
+import { NewsletterServiceMock } from '../../test/mock/services/newsletter.mock.service';
 
 describe('AdminController', () => {
   let controller: AdminController;
@@ -21,10 +27,19 @@ describe('AdminController', () => {
     const module: TestingModule = await Test.createTestingModule({
       imports: [ConfigurationModule, HttpModule, SearchModule],
       providers: [
-        UsersService,
-        StructuresService,
+        {
+          provide: UsersService,
+          useClass: UsersServiceMock,
+        },
+        {
+          provide: StructuresService,
+          useClass: StructuresServiceMock,
+        },
+        {
+          provide: NewsletterService,
+          useClass: NewsletterServiceMock,
+        },
         StructuresSearchService,
-        NewsletterService,
         MailerService,
         {
           provide: getModelToken('User'),
@@ -40,7 +55,12 @@ describe('AdminController', () => {
         },
       ],
       controllers: [AdminController],
-    }).compile();
+    })
+      .overrideGuard(JwtAuthGuard)
+      .useValue(mockJwtAuthGuard)
+      .overrideGuard(RolesGuard)
+      .useValue(mockRoleGuard)
+      .compile();
 
     controller = module.get<AdminController>(AdminController);
   });
@@ -50,36 +70,124 @@ describe('AdminController', () => {
   });
 
   it('should get pending attachments', async () => {
-    const result = [
-      { name: 'MJC Route de vienne', address: '14 chemin des platanes' },
-      { name: 'Mairie Lyon 7eme', address: '21 boulevard martin' },
-    ];
-    jest.spyOn(controller, 'getPendingAttachments').mockImplementation(async (): Promise<any> => result);
-    expect(await controller.getPendingAttachments()).toBe(result);
+    expect((await controller.getPendingAttachments()).length).toBe(2);
+    expect(Object.keys((await controller.getPendingAttachments())[0]).length).toBe(3);
+  });
+
+  describe('Pending structures validation', () => {
+    it('should validate pending structure', async () => {
+      const pendingStructureTest = {
+        structureId: '6093ba0e2ab5775cfc01ed3e',
+        structureName: 'test',
+        userEmail: 'jean.paul@mii.com',
+      };
+      expect((await controller.validatePendingStructure(pendingStructureTest)).length).toBe(2);
+      expect(Object.keys((await controller.validatePendingStructure(pendingStructureTest))[0]).length).toBe(3);
+    });
+
+    it('should get structure does not exist', async () => {
+      const pendingStructureTest = {
+        structureId: '1093ba0e2ab5775cfc01z2ki',
+        structureName: 'test',
+        userEmail: 'jean.paul@mii.com',
+      };
+      try {
+        await controller.validatePendingStructure(pendingStructureTest);
+      } catch (e) {
+        expect(e.message).toBe('Structure does not exist');
+        expect(e.status).toBe(404);
+      }
+    });
+  });
+
+  describe('Pending structures cancel', () => {
+    it('should refuse pending structure', async () => {
+      const pendingStructureTest = {
+        structureId: '6093ba0e2ab5775cfc01ed3e',
+        structureName: 'test',
+        userEmail: 'jean.paul@mii.com',
+      };
+      expect((await controller.refusePendingStructure(pendingStructureTest)).length).toBe(2);
+      expect(Object.keys((await controller.refusePendingStructure(pendingStructureTest))[0]).length).toBe(3);
+    });
+
+    it('should get structure does not exist', async () => {
+      const pendingStructureTest = {
+        structureId: '1093ba0e2ab5775cfc01z2ki',
+        structureName: 'test',
+        userEmail: 'jean.paul@mii.com',
+      };
+      try {
+        await controller.refusePendingStructure(pendingStructureTest);
+      } catch (e) {
+        expect(e.message).toBe('Structure does not exist');
+        expect(e.status).toBe(404);
+      }
+    });
+  });
+
+  describe('Delete user', () => {
+    it('should delete user', async () => {
+      expect((await controller.deleteUser({ id: 'tsfsf6296' })).email).toBe('pauline.dupont@mii.com');
+    });
+    it('should return unexisting user', async () => {
+      try {
+        await controller.deleteUser({ id: 'userdoesnotexist' });
+      } catch (e) {
+        expect(e.message).toBe('Invalid user id');
+        expect(e.status).toBe(400);
+      }
+    });
+  });
+
+  describe('Search user', () => {
+    it('should return all users, empty string', async () => {
+      expect((await controller.searchUsers({ searchString: '' })).length).toBe(2);
+    });
+    it('should return all users, null input', async () => {
+      expect((await controller.searchUsers({ searchString: null })).length).toBe(2);
+    });
+    it('should one user', async () => {
+      expect((await controller.searchUsers({ searchString: 'a@a.com' })).length).toBe(1);
+      expect((await controller.searchUsers({ searchString: 'a@a.com' }))[0].email).toBe('a@a.com');
+    });
+    it('should no user', async () => {
+      expect((await controller.searchUsers({ searchString: 'dfqfqsfqfqfa@a.com' })).length).toBe(0);
+    });
+  });
+
+  describe('Search user newleetter subscription', () => {
+    it('should return all subscribed users, empty string', async () => {
+      expect((await controller.getNewsletterSubscriptions({ searchString: '' })).length).toBe(3);
+    });
+    it('should return all subscribed users, null input', async () => {
+      expect((await controller.getNewsletterSubscriptions({ searchString: null })).length).toBe(3);
+    });
+    it('should find one user', async () => {
+      expect((await controller.getNewsletterSubscriptions({ searchString: 'a@a.com' })).length).toBe(1);
+      expect((await controller.getNewsletterSubscriptions({ searchString: 'a@a.com' }))[0].email).toBe('a@a.com');
+    });
+    it('should find no user', async () => {
+      expect((await controller.getNewsletterSubscriptions({ searchString: 'adgdgsdg@a.com' })).length).toBe(0);
+    });
   });
 
-  it('should validate pending structure', async () => {
-    const result = [{ name: 'MJC Route de vienne', address: '14 chemin des platanes' }];
-    const structure: PendingStructureDto = {
-      userEmail: 'martin@mjc.fr',
-      structureId: '1',
-      structureName: 'MJC Route de vienne',
-    };
-    jest.spyOn(controller, 'validatePendingStructure').mockImplementation(async (): Promise<any> => result);
-    expect(await controller.validatePendingStructure(structure)).toBe(result);
+  it('should count user subscribed to newsletter', async () => {
+    expect(await controller.countNewsletterSubscriptions()).toBe(246);
   });
 
-  it('should refuse pending structure', async () => {
-    const result = [
-      { name: 'MJC Route de vienne', address: '14 chemin des platanes' },
-      { name: 'Mairie Lyon 7eme', address: '21 boulevard martin' },
-    ];
-    const structure: PendingStructureDto = {
-      userEmail: 'martin@mjc.fr',
-      structureId: '1',
-      structureName: 'MJC Route de vienne',
-    };
-    jest.spyOn(controller, 'refusePendingStructure').mockImplementation(async (): Promise<any> => result);
-    expect(await controller.refusePendingStructure(structure)).toBe(result);
+  describe('Search delete a user subscription', () => {
+    it('should return a deleted object', async () => {
+      expect((await controller.unsubscribeUserFromNewsletter('a@a.com')).email).toBe('a@a.com');
+      expect(Object.keys(await controller.unsubscribeUserFromNewsletter('a@a.com')).length).toBe(4);
+    });
+    it('should throw an error', async () => {
+      try {
+        await controller.unsubscribeUserFromNewsletter('test@test.com');
+      } catch (e) {
+        expect(e.message).toBe('Invalid  email');
+        expect(e.status).toBe(401);
+      }
+    });
   });
 });
diff --git a/src/admin/admin.controller.ts b/src/admin/admin.controller.ts
index f35cae88243539fc12daa9aa0adf6c9f47499cb3..ea3fb601bd80e8a72b7e46d2305e04f79dbf3264 100644
--- a/src/admin/admin.controller.ts
+++ b/src/admin/admin.controller.ts
@@ -1,6 +1,6 @@
-import { Body, Delete, Param, Controller, Get, Post, UseGuards } from '@nestjs/common';
 import { ApiOperation, ApiParam } from '@nestjs/swagger';
 import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
+import { Body, Delete, Param, Controller, Get, Post, UseGuards, HttpStatus } from '@nestjs/common';
 import { NewsletterSubscription } from '../newsletter/newsletter-subscription.schema';
 import { NewsletterService } from '../newsletter/newsletter.service';
 import { StructuresService } from '../structures/services/structures.service';
@@ -63,6 +63,9 @@ export class AdminController {
   @ApiOperation({ description: 'Validate structure ownership' })
   public async validatePendingStructure(@Body() pendingStructureDto: PendingStructureDto) {
     const structure = await this.structuresService.findOne(pendingStructureDto.structureId);
+    if (!structure || structure.deletedAt) {
+      throw new HttpException('Structure does not exist', HttpStatus.NOT_FOUND);
+    }
     await this.usersService.validatePendingStructure(
       pendingStructureDto.userEmail,
       pendingStructureDto.structureId,
@@ -86,6 +89,9 @@ export class AdminController {
   @ApiOperation({ description: 'Refuse structure ownership' })
   public async refusePendingStructure(@Body() pendingStructureDto: PendingStructureDto) {
     const structure = await this.structuresService.findOne(pendingStructureDto.structureId);
+    if (!structure || structure.deletedAt) {
+      throw new HttpException('Structure does not exist', HttpStatus.NOT_FOUND);
+    }
     await this.usersService.validatePendingStructure(
       pendingStructureDto.userEmail,
       pendingStructureDto.structureId,
@@ -123,7 +129,7 @@ export class AdminController {
   @Roles('admin')
   @Post('searchUsers')
   public async searchUsers(@Body() searchString: { searchString: string }) {
-    if (searchString && searchString.searchString.length > 0)
+    if (searchString && searchString.searchString && searchString.searchString.length > 0)
       return this.usersService.searchUsers(searchString.searchString);
     else return this.usersService.findAll();
   }
@@ -132,7 +138,7 @@ export class AdminController {
   @Roles('admin')
   @Post('searchNewsletterSubscriptions')
   public async getNewsletterSubscriptions(@Body() searchString: { searchString: string }) {
-    if (searchString && searchString.searchString.length > 0)
+    if (searchString && searchString.searchString && searchString.searchString.length > 0)
       return this.newsletterService.searchNewsletterSubscription(searchString.searchString);
     else return this.newsletterService.findAll();
   }
diff --git a/src/admin/dto/pending-structure.dto.ts b/src/admin/dto/pending-structure.dto.ts
index e07fd91f860cc5468be1e2449ce05897a26b1465..1f735be0f4256472524dba43286ab53cba836fcc 100644
--- a/src/admin/dto/pending-structure.dto.ts
+++ b/src/admin/dto/pending-structure.dto.ts
@@ -1,5 +1,5 @@
 import { ApiProperty } from '@nestjs/swagger';
-import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
+import { IsEmail, IsMongoId, IsNotEmpty, IsString } from 'class-validator';
 
 export class PendingStructureDto {
   @IsNotEmpty()
@@ -8,7 +8,7 @@ export class PendingStructureDto {
   readonly userEmail: string;
 
   @IsNotEmpty()
-  @IsString()
+  @IsMongoId()
   @ApiProperty({ type: String })
   readonly structureId: string;
 
diff --git a/src/auth/auth.controller.spec.ts b/src/auth/auth.controller.spec.ts
index 184f7449c5b02fa817051b043378f086ae9c8f79..c92f1b8b66947641325ba1b79cd8d0c5f266f542 100644
--- a/src/auth/auth.controller.spec.ts
+++ b/src/auth/auth.controller.spec.ts
@@ -2,6 +2,7 @@ import { JwtModule } from '@nestjs/jwt';
 import { getModelToken } from '@nestjs/mongoose';
 import { PassportModule } from '@nestjs/passport';
 import { Test, TestingModule } from '@nestjs/testing';
+import { AuthServiceMock } from '../../test/mock/services/auth.mock.service';
 import { ConfigurationModule } from '../configuration/configuration.module';
 import { MailerModule } from '../mailer/mailer.module';
 import { User } from '../users/schemas/user.schema';
@@ -9,11 +10,9 @@ import { UsersService } from '../users/users.service';
 import { AuthController } from './auth.controller';
 import { AuthService } from './auth.service';
 import { LoginDto } from './login-dto';
-import { HttpException, HttpStatus } from '@nestjs/common';
 
 describe('AuthController', () => {
   let controller: AuthController;
-  // let service: AuthService;
 
   beforeEach(async () => {
     const module: TestingModule = await Test.createTestingModule({
@@ -30,6 +29,10 @@ describe('AuthController', () => {
       providers: [
         AuthService,
         UsersService,
+        {
+          provide: AuthService,
+          useClass: AuthServiceMock,
+        },
         {
           provide: getModelToken('User'),
           useValue: User,
@@ -45,16 +48,29 @@ describe('AuthController', () => {
   });
 
   it('should login valid user', async () => {
-    const result = { username: 'paula.dubois@mii.com', token: 'tok3N', name: 'paula', surname: 'dubois' };
-    jest.spyOn(controller, 'login').mockImplementation(async (): Promise<{ username; name; surname; token }> => result);
     const loginCredentials: LoginDto = { email: 'paula.dubois@mii.com', password: process.env.USER_PWD };
-    expect(await controller.login(loginCredentials)).toBe(result);
+    const result = await controller.login(loginCredentials);
+    expect(result).toStrictEqual({
+      _id: 'tsfsf6296',
+      email: 'pauline.dupont@mii.com',
+      emailVerified: true,
+      name: 'DUBOIS',
+      password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
+      role: 0,
+      surname: 'Paula',
+      validationToken:
+        'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+    });
   });
 
-  it('should not login valid user', async () => {
-    const result: HttpException = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
-    jest.spyOn(controller, 'login').mockImplementation(async (): Promise<any> => result);
-    const loginCredentials: LoginDto = { email: 'paula.dubois@mii.com', password: process.env.USER_PWD };
-    expect(await controller.login(loginCredentials)).toBe(result);
+  it('should not login invalid user', async () => {
+    const loginCredentials: LoginDto = { email: 'jacques.dupont@mii.com', password: process.env.USER_PWD };
+    try {
+      await controller.login(loginCredentials);
+    } catch (e) {
+      expect(e.response).toBe('Invalid credentials');
+      expect(e.message).toBe('Invalid credentials');
+      expect(e.status).toBe(401);
+    }
   });
 });
diff --git a/src/auth/auth.service.spec.ts b/src/auth/auth.service.spec.ts
index 2e85b13e99460b761605d520d20d936392dc2d9c..188c44616d058d7ca507edd84ae321f047653bb0 100644
--- a/src/auth/auth.service.spec.ts
+++ b/src/auth/auth.service.spec.ts
@@ -1,11 +1,10 @@
-import { HttpException, HttpStatus } from '@nestjs/common';
-import { JwtModule } from '@nestjs/jwt';
-import { getModelToken } from '@nestjs/mongoose';
+import { JwtService } from '@nestjs/jwt';
 import { PassportModule } from '@nestjs/passport';
 import { Test, TestingModule } from '@nestjs/testing';
+import { JwtServiceMock } from '../../test/mock/services/jwt.mock.service';
+import { UsersServiceMock } from '../../test/mock/services/user.mock.service';
 import { ConfigurationModule } from '../configuration/configuration.module';
 import { MailerModule } from '../mailer/mailer.module';
-import { User } from '../users/schemas/user.schema';
 import { UsersService } from '../users/users.service';
 import { AuthService } from './auth.service';
 import { LoginDto } from './login-dto';
@@ -15,21 +14,16 @@ describe('AuthService', () => {
 
   beforeEach(async () => {
     const module: TestingModule = await Test.createTestingModule({
-      imports: [
-        PassportModule,
-        MailerModule,
-        ConfigurationModule,
-        JwtModule.register({
-          secret: process.env.JWT_SECRET,
-          signOptions: { expiresIn: '86400s' }, // 24h validity
-        }),
-      ],
+      imports: [PassportModule, MailerModule, ConfigurationModule],
       providers: [
         AuthService,
-        UsersService,
         {
-          provide: getModelToken('User'),
-          useValue: User,
+          provide: UsersService,
+          useClass: UsersServiceMock,
+        },
+        {
+          provide: JwtService,
+          useClass: JwtServiceMock,
         },
       ],
     }).compile();
@@ -52,45 +46,95 @@ describe('AuthService', () => {
         role: 0,
       };
       const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
-      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
-      expect(await service.validateUser(loginDto)).toBe(result);
+      expect(await service.validateUser(loginDto)).toStrictEqual(result);
     });
 
     it('should not validateUser', async () => {
-      const result = null;
-      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
-      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
-      expect(await service.validateUser(loginDto)).toBe(result);
+      const loginDto: LoginDto = { email: 'tom.dupont@mii.com', password: 'test1A!!!' }; //NOSONAR
+      expect(await service.validateUser(loginDto)).toBe(null);
     });
   });
 
   describe('login', () => {
-    it('should login user jacques.dupont@mii.com', async () => {
-      const result = { username: ' jacques.dupont@mii.com', token: 'tok3n!1sfq' };
-      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
-      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<{ username; token }> => result);
-      expect(await service.validateUser(loginDto)).toBe(result);
+    it('should login user pauline.dupont@mii.com', async () => {
+      // Token creation mock
+      const token = {
+        accessToken:
+          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
+        expiresAt: '2021-05-04T15:35:06.663+02:00',
+      };
+      const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken');
+      _createToken.mockImplementation(() => token);
+
+      const loginDto: LoginDto = { email: 'pauline.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
+      expect(await service.login(loginDto)).toStrictEqual({
+        accessToken:
+          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
+        expiresAt: '2021-05-04T15:35:06.663+02:00',
+        username: 'pauline.dupont@mii.com',
+        name: 'DUPONT',
+        surname: 'Pauline',
+      });
     });
 
-    it('should not login jacques.dupont@mii.com, email not verified', async () => {
-      const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+    it('should login not login user jacques.dupont@mii.com because email is not verified', async () => {
+      // Token creation mock
+      const token = {
+        accessToken:
+          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
+        expiresAt: '2021-05-04T15:35:06.663+02:00',
+      };
+      const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken');
+      _createToken.mockImplementation(() => token);
+
       const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
-      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
-      expect(await service.validateUser(loginDto)).toBe(result);
+      try {
+        await service.login(loginDto);
+      } catch (e) {
+        expect(e.response).toBe('Invalid credentials');
+        expect(e.message).toBe('Invalid credentials');
+        expect(e.status).toBe(401);
+      }
     });
 
-    it('should not login jacques.dupont@mii.com, bad password', async () => {
-      const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
-      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
-      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
-      expect(await service.validateUser(loginDto)).toBe(result);
+    it('should login not login user toto@mii.com because email does not exist', async () => {
+      // Token creation mock
+      const token = {
+        accessToken:
+          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
+        expiresAt: '2021-05-04T15:35:06.663+02:00',
+      };
+      const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken');
+      _createToken.mockImplementation(() => token);
+
+      const loginDto: LoginDto = { email: 'toto@mii.com', password: 'test1A!!' }; //NOSONAR
+      try {
+        await service.login(loginDto);
+      } catch (e) {
+        expect(e.response).toBe('Invalid credentials');
+        expect(e.message).toBe('Invalid credentials');
+        expect(e.status).toBe(401);
+      }
     });
 
-    it('should not login jacques.dupont@mii.com, username does not exist', async () => {
-      const result = new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
-      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
-      jest.spyOn(service, 'validateUser').mockImplementation(async (): Promise<any> => result);
-      expect(await service.validateUser(loginDto)).toBe(result);
+    it('should login not login user toto@mii.com because password is invalid', async () => {
+      // Token creation mock
+      const token = {
+        accessToken:
+          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4',
+        expiresAt: '2021-05-04T15:35:06.663+02:00',
+      };
+      const _createToken = jest.spyOn(AuthService.prototype as any, '_createToken');
+      _createToken.mockImplementation(() => token);
+
+      const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: '1' }; //NOSONAR
+      try {
+        await service.login(loginDto);
+      } catch (e) {
+        expect(e.response).toBe('Invalid credentials');
+        expect(e.message).toBe('Invalid credentials');
+        expect(e.status).toBe(401);
+      }
     });
   });
 });
diff --git a/src/categories/controllers/categories-accompagnement.controller.spec.ts b/src/categories/controllers/categories-accompagnement.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f3035f99d088bc9a6cf36db92b231f3f792eac7b
--- /dev/null
+++ b/src/categories/controllers/categories-accompagnement.controller.spec.ts
@@ -0,0 +1,65 @@
+import { getModelToken } from '@nestjs/mongoose';
+import { Test, TestingModule } from '@nestjs/testing';
+import { mockJwtAuthGuard } from '../../../test/mock/guards/jwt-auth.mock.guard';
+import { mockRoleGuard } from '../../../test/mock/guards/role.mock.guard';
+import { CategoriesAccompagnementServiceMock } from '../../../test/mock/services/categoriesAccompagnement.mock.service';
+import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
+import { RolesGuard } from '../../users/guards/roles.guard';
+import { CreateCategoriesAccompagnement } from '../dto/create-categoriesAccompagnement.dto';
+import { CategoriesAccompagnement } from '../schemas/categoriesAccompagnement.schema';
+import { CategoriesAccompagnementService } from '../services/categories-accompagnement.service';
+import { CategoriesAccompagnementController } from './categories-accompagnement.controller';
+
+describe('CategoriesAccompagnementController', () => {
+  let controller: CategoriesAccompagnementController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [],
+      providers: [
+        {
+          provide: CategoriesAccompagnementService,
+          useClass: CategoriesAccompagnementServiceMock,
+        },
+        {
+          provide: getModelToken('CategoriesAccompagnement'),
+          useValue: CategoriesAccompagnement,
+        },
+      ],
+      controllers: [CategoriesAccompagnementController],
+    })
+      .overrideGuard(JwtAuthGuard)
+      .useValue(mockJwtAuthGuard)
+      .overrideGuard(RolesGuard)
+      .useValue(mockRoleGuard)
+      .compile();
+
+    controller = module.get<CategoriesAccompagnementController>(CategoriesAccompagnementController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+
+  it('should find all accompagnements', async () => {
+    expect((await controller.findAll()).length).toBe(1);
+    expect((await controller.findAll())[0].modules.length).toBe(8);
+  });
+
+  it('should create accompagnements', async () => {
+    const data: CreateCategoriesAccompagnement = {
+      name: 'test',
+      modules: [
+        {
+          id: 'tst',
+          text: 'Test',
+        },
+        {
+          id: 'antst',
+          text: 'Another test',
+        },
+      ],
+    };
+    expect((await controller.create(data)).modules.length).toBe(2);
+  });
+});
diff --git a/src/categories/controllers/categories-accompagnement.controller.ts b/src/categories/controllers/categories-accompagnement.controller.ts
index 66ea90536b84624505ba92e826f7b3cbe9fa2b9d..51d7f482339ed2ff952db8e456ef72f8b6012556 100644
--- a/src/categories/controllers/categories-accompagnement.controller.ts
+++ b/src/categories/controllers/categories-accompagnement.controller.ts
@@ -1,15 +1,20 @@
-import { Body, Controller, Get, Post } from '@nestjs/common';
+import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
 import { CategoriesAccompagnementService } from '../services/categories-accompagnement.service';
 import { CreateCategoriesAccompagnement } from '../dto/create-categoriesAccompagnement.dto';
 import { CategoriesAccompagnement } from '../schemas/categoriesAccompagnement.schema';
+import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
+import { Roles } from '../../users/decorators/roles.decorator';
+import { RolesGuard } from '../../users/guards/roles.guard';
 
 @Controller('categories/categoriesAccompagnement')
 export class CategoriesAccompagnementController {
   constructor(private readonly categoriesAccompagnementService: CategoriesAccompagnementService) {}
 
   @Post()
-  public async create(@Body() createStructureDto: CreateCategoriesAccompagnement) {
-    await this.categoriesAccompagnementService.create(createStructureDto);
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  public async create(@Body() createStructureDto: CreateCategoriesAccompagnement): Promise<CategoriesAccompagnement> {
+    return this.categoriesAccompagnementService.create(createStructureDto);
   }
 
   @Get()
diff --git a/src/categories/controllers/categories-formations.controller.spec.ts b/src/categories/controllers/categories-formations.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c9a1d3099db52bcb7c9f21e23b70e463a4746a3
--- /dev/null
+++ b/src/categories/controllers/categories-formations.controller.spec.ts
@@ -0,0 +1,55 @@
+import { getModelToken } from '@nestjs/mongoose';
+import { Test, TestingModule } from '@nestjs/testing';
+import { CategoriesFormationsServiceMock } from '../../../test/mock/services/categoriesFormations.mock.service';
+import { CreateCategoriesFormations } from '../dto/create-categoriesFormations.dto';
+import { CategoriesFormations } from '../schemas/categoriesFormations.schema';
+import { CategoriesFormationsService } from '../services/categories-formations.service';
+import { CategoriesFormationsController } from './categories-formations.controller';
+
+describe('CategoriesFormationsController', () => {
+  let controller: CategoriesFormationsController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [],
+      providers: [
+        {
+          provide: CategoriesFormationsService,
+          useClass: CategoriesFormationsServiceMock,
+        },
+        {
+          provide: getModelToken('CategoriesFormations'),
+          useValue: CategoriesFormations,
+        },
+      ],
+      controllers: [CategoriesFormationsController],
+    }).compile();
+
+    controller = module.get<CategoriesFormationsController>(CategoriesFormationsController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+
+  it('should find all formations', async () => {
+    expect((await controller.findAll()).length).toBe(6);
+    expect((await controller.findAll())[0].modules.length).toBe(8);
+  });
+
+  it('should create a formation', async () => {
+    const data: CreateCategoriesFormations = {
+      name: 'test',
+      modules: [
+        {
+          id: '74',
+          display_id: '74',
+          display_name: 'Modules APTIC - n°74',
+          digest: 'Smartphones et Tablettes sous Androïd',
+          text: 'Smartphones et Tablettes sous Androïd',
+        },
+      ],
+    };
+    expect((await controller.create(data)).modules.length).toBe(2);
+  });
+});
diff --git a/src/categories/controllers/categories-formations.controller.ts b/src/categories/controllers/categories-formations.controller.ts
index 7b1ca152bc86bc15adcd48baa8e7dd4442678589..527a7ba9f33deddbcf5743dc29697f3261cd2ccf 100644
--- a/src/categories/controllers/categories-formations.controller.ts
+++ b/src/categories/controllers/categories-formations.controller.ts
@@ -1,15 +1,20 @@
-import { Body, Controller, Get, Post } from '@nestjs/common';
+import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
 import { CategoriesFormationsService } from '../services/categories-formations.service';
 import { CreateCategoriesFormations } from '../dto/create-categoriesFormations.dto';
 import { CategoriesFormations } from '../schemas/categoriesFormations.schema';
+import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
+import { Roles } from '../../users/decorators/roles.decorator';
+import { RolesGuard } from '../../users/guards/roles.guard';
 
 @Controller('categories/categoriesFormations')
 export class CategoriesFormationsController {
   constructor(private readonly categoriesFormationsService: CategoriesFormationsService) {}
 
   @Post()
-  public async create(@Body() createStructureDto: CreateCategoriesFormations) {
-    await this.categoriesFormationsService.create(createStructureDto);
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  public async create(@Body() createStructureDto: CreateCategoriesFormations): Promise<CategoriesFormations> {
+    return this.categoriesFormationsService.create(createStructureDto);
   }
 
   @Get()
diff --git a/src/categories/controllers/categories-others.controller.spec.ts b/src/categories/controllers/categories-others.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a7acc20479fe13a2740cc7d09b92459497b96ea
--- /dev/null
+++ b/src/categories/controllers/categories-others.controller.spec.ts
@@ -0,0 +1,51 @@
+import { getModelToken } from '@nestjs/mongoose';
+import { Test, TestingModule } from '@nestjs/testing';
+import { CategoriesOthersServiceMock } from '../../../test/mock/services/categoriesOthers.mock.service';
+import { CategoriesOthers } from '../schemas/categoriesOthers.schema';
+import { CategoriesOthersService } from '../services/categories-others.service';
+import { CategoriesOthersController } from './categories-others.controller';
+
+describe('CategoriesOthersController', () => {
+  let controller: CategoriesOthersController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [],
+      providers: [
+        {
+          provide: CategoriesOthersService,
+          useClass: CategoriesOthersServiceMock,
+        },
+        {
+          provide: getModelToken('CategoriesOthers'),
+          useValue: CategoriesOthers,
+        },
+      ],
+      controllers: [CategoriesOthersController],
+    }).compile();
+
+    controller = module.get<CategoriesOthersController>(CategoriesOthersController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+
+  it('should find all formations', async () => {
+    expect((await controller.findAll()).length).toBe(1);
+    expect((await controller.findAll())[0].modules.length).toBe(6);
+  });
+
+  it('should create a formation', async () => {
+    const data: CategoriesOthers = {
+      name: 'test',
+      modules: [
+        {
+          id: 'toutPublic',
+          text: 'Tout public',
+        },
+      ],
+    };
+    expect((await controller.create(data)).modules.length).toBe(1);
+  });
+});
diff --git a/src/categories/controllers/categories-others.controller.ts b/src/categories/controllers/categories-others.controller.ts
index 6e284e397b877f14417040ba36d36516bb15e1eb..b45f1530ed2ef443f52ba5a9f9b5cee6b720fc66 100644
--- a/src/categories/controllers/categories-others.controller.ts
+++ b/src/categories/controllers/categories-others.controller.ts
@@ -1,15 +1,20 @@
-import { Body, Controller, Get, Post } from '@nestjs/common';
+import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
 import { CategoriesOthersService } from '../services/categories-others.service';
 import { CreateCategoriesOthers } from '../dto/create-categoriesOthers.dto';
 import { CategoriesOthers } from '../schemas/categoriesOthers.schema';
+import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
+import { Roles } from '../../users/decorators/roles.decorator';
+import { RolesGuard } from '../../users/guards/roles.guard';
 
 @Controller('categories/categoriesOthers')
 export class CategoriesOthersController {
   constructor(private readonly categoriesOthersService: CategoriesOthersService) {}
 
   @Post()
-  public async create(@Body() createStructureDto: CreateCategoriesOthers) {
-    await this.categoriesOthersService.create(createStructureDto);
+  @UseGuards(JwtAuthGuard, RolesGuard)
+  @Roles('admin')
+  public async create(@Body() createStructureDto: CreateCategoriesOthers): Promise<CategoriesOthers> {
+    return this.categoriesOthersService.create(createStructureDto);
   }
 
   @Get()
diff --git a/src/categories/schemas/categoriesFormationsModule.schema.ts b/src/categories/schemas/categoriesFormationsModule.schema.ts
index 6be322e9c34eb02ca06e497d7d54ab98830dde44..4eaedd6da48c2012e6e63684db7febaf75102816 100644
--- a/src/categories/schemas/categoriesFormationsModule.schema.ts
+++ b/src/categories/schemas/categoriesFormationsModule.schema.ts
@@ -4,14 +4,11 @@ import { Document } from 'mongoose';
 export type CategoriesFormationsModuleDocument = CategoriesFormationsModule & Document;
 
 export class CategoriesFormationsModule {
-  id: number;
+  id: string;
   display_id: string;
   display_name: string;
   digest: string;
   text: string;
-  url: string;
-  receipt_time: string;
-  last_update_time: string;
 }
 
 export const CategoriesFormationsModuleSchema = SchemaFactory.createForClass(CategoriesFormationsModule);
diff --git a/src/categories/services/categories-accompagnement.service.spec.ts b/src/categories/services/categories-accompagnement.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2690850884d95e003009cd4395d8d7125889d03b
--- /dev/null
+++ b/src/categories/services/categories-accompagnement.service.spec.ts
@@ -0,0 +1,27 @@
+import { getModelToken } from '@nestjs/mongoose';
+import { Test, TestingModule } from '@nestjs/testing';
+import { CategoriesAccompagnement } from '../schemas/categoriesAccompagnement.schema';
+import { CategoriesAccompagnementService } from './categories-accompagnement.service';
+
+describe('CategoriesAccompagnementService', () => {
+  let service: CategoriesAccompagnementService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [],
+      providers: [
+        CategoriesAccompagnementService,
+        {
+          provide: getModelToken('CategoriesAccompagnement'),
+          useValue: CategoriesAccompagnement,
+        },
+      ],
+    }).compile();
+
+    service = module.get<CategoriesAccompagnementService>(CategoriesAccompagnementService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+});
diff --git a/src/categories/services/categories-formations.service.spec.ts b/src/categories/services/categories-formations.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f079fdd4611597a976174206557113ead3b3194
--- /dev/null
+++ b/src/categories/services/categories-formations.service.spec.ts
@@ -0,0 +1,27 @@
+import { getModelToken } from '@nestjs/mongoose';
+import { Test, TestingModule } from '@nestjs/testing';
+import { CategoriesFormations } from '../schemas/categoriesFormations.schema';
+import { CategoriesFormationsService } from './categories-formations.service';
+
+describe('CategoriesFormationsService', () => {
+  let service: CategoriesFormationsService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [],
+      providers: [
+        CategoriesFormationsService,
+        {
+          provide: getModelToken('CategoriesFormations'),
+          useValue: CategoriesFormations,
+        },
+      ],
+    }).compile();
+
+    service = module.get<CategoriesFormationsService>(CategoriesFormationsService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+});
diff --git a/src/categories/services/categories-others.service.spec.ts b/src/categories/services/categories-others.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7c25eb3618f8bd75287bb29c7ebc52ba3256f33c
--- /dev/null
+++ b/src/categories/services/categories-others.service.spec.ts
@@ -0,0 +1,27 @@
+import { getModelToken } from '@nestjs/mongoose';
+import { Test, TestingModule } from '@nestjs/testing';
+import { CategoriesOthers } from '../schemas/categoriesOthers.schema';
+import { CategoriesOthersService } from './categories-others.service';
+
+describe('CategoriesFormationsService', () => {
+  let service: CategoriesOthersService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [],
+      providers: [
+        CategoriesOthersService,
+        {
+          provide: getModelToken('CategoriesOthers'),
+          useValue: CategoriesOthers,
+        },
+      ],
+    }).compile();
+
+    service = module.get<CategoriesOthersService>(CategoriesOthersService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+});
diff --git a/src/configuration/configuration.service.spec.ts b/src/configuration/configuration.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a5ddba0b94c2d6b95b5846ca549160d01bf42275
--- /dev/null
+++ b/src/configuration/configuration.service.spec.ts
@@ -0,0 +1,88 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { ConfigurationService } from './configuration.service';
+
+describe('ConfigurationService', () => {
+  let service: ConfigurationService;
+
+  process.env.NODE_ENV = 'local';
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [],
+      providers: [ConfigurationService],
+    }).compile();
+
+    service = module.get<ConfigurationService>(ConfigurationService);
+  });
+
+  describe('initialisation', () => {
+    const OLD_ENV = process.env;
+    beforeEach(async () => {
+      jest.resetModules(); // Most important - it clears the cache
+      process.env = { ...OLD_ENV }; // Make a copy
+      process.env.NODE_ENV = 'dev';
+    });
+
+    afterAll(() => {
+      process.env = OLD_ENV; // Restore old environment
+    });
+
+    it('should be defined', () => {
+      expect(service).toBeDefined();
+    });
+
+    it('should init with dev conf', () => {
+      process.env.NODE_ENV = 'dev';
+      expect(service.config.host).toBe('resin-dev.grandlyon.com');
+    });
+  });
+
+  describe('initialisation production', () => {
+    const OLD_ENV = process.env;
+    beforeEach(async () => {
+      jest.resetModules(); // Most important - it clears the cache
+      process.env = { ...OLD_ENV }; // Make a copy
+      process.env.NODE_ENV = 'production';
+    });
+
+    afterAll(() => {
+      process.env = OLD_ENV; // Restore old environment
+    });
+
+    it('should be defined', () => {
+      expect(service).toBeDefined();
+    });
+
+    it('should init with prod conf', () => {
+      process.env.NODE_ENV = 'production';
+      expect(service.config.host).toBe('resin.grandlyon.com');
+    });
+  });
+
+  describe('validateUser', () => {
+    it('should be local conf', () => {
+      process.env.NODE_ENV = 'local';
+      expect(service.isLocalConf()).toBe(true);
+    });
+  });
+
+  describe('get config', () => {
+    it('should get config', () => {
+      const config = service.config;
+      expect(Object.keys(config).length).toBe(9);
+      expect(Object.keys(config)).toEqual(
+        expect.arrayContaining([
+          'url',
+          'token',
+          'host',
+          'protocol',
+          'port',
+          'from',
+          'from_name',
+          'replyTo',
+          'templates',
+        ])
+      );
+    });
+  });
+});
diff --git a/src/mailer/mailer.service.spec.ts b/src/mailer/mailer.service.spec.ts
index 2fd12e46668bdc0cc4a32940715c2a7fc9043cdf..f3e369fba79fd3b542e6d4e5865b05db6103e402 100644
--- a/src/mailer/mailer.service.spec.ts
+++ b/src/mailer/mailer.service.spec.ts
@@ -1,10 +1,15 @@
-import { HttpModule } from '@nestjs/common';
+import { HttpModule, HttpService } from '@nestjs/common';
 import { Test, TestingModule } from '@nestjs/testing';
+import { of, throwError } from 'rxjs';
+import { AxiosResponse } from 'axios';
 import { ConfigurationService } from '../configuration/configuration.service';
 import { MailerService } from './mailer.service';
+import * as fs from 'fs';
+import * as path from 'path';
 
 describe('MailerService', () => {
   let service: MailerService;
+  let httpService: HttpService;
 
   beforeEach(async () => {
     const module: TestingModule = await Test.createTestingModule({
@@ -13,33 +18,104 @@ describe('MailerService', () => {
     }).compile();
 
     service = module.get<MailerService>(MailerService);
+    httpService = module.get<HttpService>(HttpService);
   });
 
   it('should be defined', () => {
     expect(service).toBeDefined();
   });
-  
-  it('should send email', async () => {
-    const result = "AxiosResponse"
-    jest.spyOn(service, 'send').mockImplementation(async (): Promise<any> => result);
-    expect(await service.send("to", "subject", "html")).toBe(result);
+
+  describe('email sending', () => {
+    it('should send email', async () => {
+      const result: AxiosResponse = {
+        data: {
+          status: 200,
+          content: {
+            success: true,
+            response: [7010],
+          },
+        },
+        status: 200,
+        statusText: 'OK',
+        headers: {},
+        config: {},
+      };
+      jest.spyOn(httpService, 'post').mockImplementationOnce(() => of(result));
+      expect(await service.send('a@a.com', 'test', '<p>This is a test</p>')).toBe(result);
+    });
+
+    it('should not send email', async () => {
+      const result: AxiosResponse = {
+        data: {
+          errors: ['Subject cannot be blank'],
+          detail: 'There was a validation error',
+          status: 400,
+          type: 'validation_error',
+          title: 'There was a validation error',
+        },
+        status: 400,
+        statusText: 'KO',
+        headers: {},
+        config: {},
+      };
+      jest.spyOn(httpService, 'post').mockImplementationOnce(() => throwError(result));
+      try {
+        await service.send('a@a.com', 'test', '<p>This is a test</p>');
+      } catch (e) {
+        expect(e.data.detail).toBe('There was a validation error');
+        expect(e.status).toBe(400);
+      }
+    });
   });
 
-  it('should get template location', async () => {
-    const result = "/path/to/template";
-    jest.spyOn(service, 'getTemplateLocation').mockImplementation(() => {return result});
-    expect(await service.getTemplateLocation("filename")).toBe(result);
+  describe('template location', () => {
+    it('should get template location', async () => {
+      jest.spyOn(path, 'join').mockImplementationOnce(() => '/path/to/template');
+      jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => true);
+      expect(service.getTemplateLocation('filename')).toBe('/path/to/template');
+    });
+
+    it('should not get template location', async () => {
+      const filename = 'templateFile';
+      jest.spyOn(path, 'join').mockImplementationOnce(() => '/path/to/filename');
+      jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => false);
+      try {
+        expect(service.getTemplateLocation(filename)).toBe('/path/to/template');
+      } catch (e) {
+        expect(e.message).toBe(`Email template '${filename}' cannot be found in ./src/mailer/mail-templates`);
+      }
+    });
   });
 
-  it('should load Json Config', async () => {
-    const result = null;
-    jest.spyOn(service, 'loadJsonConfig').mockImplementation(async (): Promise<any> => result);
-    expect(await service.loadJsonConfig("filename")).toBe(result);
+  describe('json config', () => {
+    it('should get template location', async () => {
+      jest.spyOn(path, 'join').mockImplementationOnce(() => '/path/to/template');
+      jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => true);
+      const data = { test: 'test value', value: 'this is the value' };
+      const buf = Buffer.from(JSON.stringify(data), 'utf8');
+      jest.spyOn(fs, 'readFileSync').mockImplementationOnce(() => buf);
+      jest.spyOn(JSON, 'parse').mockImplementationOnce(() => JSON.stringify(data));
+      expect(service.loadJsonConfig('filename')).toStrictEqual(JSON.stringify(data));
+    });
+
+    it('should not get template location', async () => {
+      const filename = 'templateFile';
+      jest.spyOn(path, 'join').mockImplementationOnce(() => '/path/to/filename');
+      jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => false);
+      try {
+        expect(service.loadJsonConfig(filename)).toBe('/path/to/template');
+      } catch (e) {
+        expect(e.message).toBe(
+          `Email json definition file '${filename}' cannot be found in ./src/mailer/mail-templates`
+        );
+      }
+    });
   });
 
   it('should add signature', async () => {
-    const result = "signed html";
-    jest.spyOn(service, 'addSignature').mockImplementation(() => {return result});
-    expect(await service.addSignature("html")).toBe(result);
+    const test = '<p>test email</p>';
+    expect(service.addSignature(test)).toBe(
+      '<p>test email</p><br /><br /><p>L’équipe projet inclusion numérique.</p><img src="http://localhost:4200/assets/logos/resin.jpg" alt="Logo resin" width="168" height="58"><br /><br /><p>Ce mail est automatique. Merci de ne pas y répondre.</p>'
+    );
   });
 });
diff --git a/src/mailer/mailer.service.ts b/src/mailer/mailer.service.ts
index c19d4b2b2830a1ba83c34669ab195df78c90a9ff..2914fd1383297e0f862b4405020ca8ed4b61e903 100644
--- a/src/mailer/mailer.service.ts
+++ b/src/mailer/mailer.service.ts
@@ -71,6 +71,7 @@ export class MailerService {
     }
     return ejsPath;
   }
+
   /**
    * Load email json config file from config directory and given filename. Also, check if file exists
    *
diff --git a/src/newsletter/newsletter.service.ts b/src/newsletter/newsletter.service.ts
index 57dab32d153c9d0843eca5e005a49da16123853f..89020eddbaa0808f25f0cedd9c30fba7bc29d1f0 100644
--- a/src/newsletter/newsletter.service.ts
+++ b/src/newsletter/newsletter.service.ts
@@ -1,6 +1,6 @@
 import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
 import { InjectModel } from '@nestjs/mongoose';
-import { Model, Types } from 'mongoose';
+import { Model } from 'mongoose';
 import { INewsletterSubscription } from './interface/newsletter-subscription.interface';
 import { NewsletterSubscription, NewsletterSubscriptionDocument } from './newsletter-subscription.schema';
 
diff --git a/src/structures/structures.controller.spec.ts b/src/structures/structures.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1eee0eaaa2d68dcb9b33924f6c2c58ad684808e2
--- /dev/null
+++ b/src/structures/structures.controller.spec.ts
@@ -0,0 +1,67 @@
+import { HttpService } from '@nestjs/common';
+import { Test, TestingModule } from '@nestjs/testing';
+import { CategoriesAccompagnementServiceMock } from '../../test/mock/services/categoriesAccompagnement.mock.service';
+import { CategoriesFormationsServiceMock } from '../../test/mock/services/categoriesFormations.mock.service';
+import { CategoriesOthersServiceMock } from '../../test/mock/services/categoriesOthers.mock.service';
+import { HttpServiceMock } from '../../test/mock/services/http.mock.service';
+import { StructuresServiceMock } from '../../test/mock/services/structures.mock.service';
+import { TempUserServiceMock } from '../../test/mock/services/tempUser.mock.service';
+import { UsersServiceMock } from '../../test/mock/services/user.mock.service';
+import { CategoriesAccompagnementService } from '../categories/services/categories-accompagnement.service';
+import { CategoriesFormationsService } from '../categories/services/categories-formations.service';
+import { CategoriesOthersService } from '../categories/services/categories-others.service';
+import { TempUserService } from '../temp-user/temp-user.service';
+import { UsersService } from '../users/users.service';
+import { StructuresService } from './services/structures.service';
+import { StructuresController } from './structures.controller';
+describe('AuthController', () => {
+  let controller: StructuresController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      imports: [],
+      controllers: [StructuresController],
+      providers: [
+        {
+          provide: HttpService,
+          useClass: HttpServiceMock,
+        },
+        {
+          provide: StructuresService,
+          useClass: StructuresServiceMock,
+        },
+        {
+          provide: UsersService,
+          useClass: UsersServiceMock,
+        },
+        {
+          provide: TempUserService,
+          useClass: TempUserServiceMock,
+        },
+        {
+          provide: CategoriesAccompagnementService,
+          useClass: CategoriesAccompagnementServiceMock,
+        },
+        {
+          provide: CategoriesOthersService,
+          useClass: CategoriesOthersServiceMock,
+        },
+        {
+          provide: CategoriesFormationsService,
+          useClass: CategoriesFormationsServiceMock,
+        },
+      ],
+    }).compile();
+
+    controller = module.get<StructuresController>(StructuresController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+
+  //TODO: test structure controler endpoint
+  //create, search, updateAccountVerified, update, findAll, findAllFormated, isClaimed
+  //updateStructureLinkedClaim, countCategories, searchAddress, find, findWithOwners
+  //delete, addOwner, join, joinValidation, removeOwner, reportStructureError
+});
diff --git a/test/mock/guards/jwt-auth.mock.guard.ts b/test/mock/guards/jwt-auth.mock.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7392d9b9b5274778a22d37dc1b683ae0802ab1e
--- /dev/null
+++ b/test/mock/guards/jwt-auth.mock.guard.ts
@@ -0,0 +1,2 @@
+import { CanActivate } from '@nestjs/common';
+export const mockJwtAuthGuard: CanActivate = { canActivate: jest.fn(() => true) };
diff --git a/test/mock/guards/role.mock.guard.ts b/test/mock/guards/role.mock.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1f93ccdc0a52c5af9006e81442e00e6327664d3c
--- /dev/null
+++ b/test/mock/guards/role.mock.guard.ts
@@ -0,0 +1,2 @@
+import { CanActivate } from '@nestjs/common';
+export const mockRoleGuard: CanActivate = { canActivate: jest.fn(() => true) };
diff --git a/test/mock/services/auth.mock.service.ts b/test/mock/services/auth.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..07d1d02da19bdb23dbcc9091f19daa557bfdc7be
--- /dev/null
+++ b/test/mock/services/auth.mock.service.ts
@@ -0,0 +1,30 @@
+import { LoginDto } from '../../../src/auth/login-dto';
+
+export class AuthServiceMock {
+  login(loginDto: LoginDto) {
+    if (loginDto.email === 'paula.dubois@mii.com') {
+      return {
+        _id: 'tsfsf6296',
+        validationToken:
+          'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+        emailVerified: true,
+        email: 'pauline.dupont@mii.com',
+        password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
+        role: 0,
+        name: 'DUBOIS',
+        surname: 'Paula',
+      };
+    }
+    if (loginDto.email === 'jacques.dupont@mii.com') {
+      return {
+        _id: 'tsfsf6296',
+        validationToken:
+          'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+        emailVerified: false,
+        email: 'jacques.dupont@mii.com',
+        role: 0,
+      };
+    }
+    return null;
+  }
+}
diff --git a/test/mock/services/categoriesAccompagnement.mock.service.ts b/test/mock/services/categoriesAccompagnement.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..00856ec270f151f9441339379269f479933c996f
--- /dev/null
+++ b/test/mock/services/categoriesAccompagnement.mock.service.ts
@@ -0,0 +1,63 @@
+export class CategoriesAccompagnementServiceMock {
+  create() {
+    return {
+      modules: [
+        {
+          id: 'tst',
+          text: 'Test',
+        },
+        {
+          id: 'antst',
+          text: 'Another test',
+        },
+      ],
+      name: 'test',
+      id: 'test',
+      __v: 0,
+    };
+  }
+
+  findAll() {
+    return [
+      {
+        modules: [
+          {
+            id: 'accompagnantCaf',
+            text: 'Accompagnant CAF',
+          },
+          {
+            id: 'poleEmploi',
+            text: 'Pôle Emploi',
+          },
+          {
+            id: 'cpam',
+            text: 'CPAM',
+          },
+          {
+            id: 'impots',
+            text: 'Impôts',
+          },
+          {
+            id: 'logement',
+            text: 'Logement',
+          },
+          {
+            id: 'carsat',
+            text: 'CARSAT',
+          },
+          {
+            id: 'demarcheMetropolitaine',
+            text: 'Démarches Métropolitaines',
+          },
+          {
+            id: 'autres',
+            text: 'Autres',
+          },
+        ],
+        name: 'Accompagnement des démarches',
+        id: 'proceduresAccompaniment',
+        __v: 0,
+      },
+    ];
+  }
+}
diff --git a/test/mock/services/categoriesFormations.mock.service.ts b/test/mock/services/categoriesFormations.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..da95bfec9da60a4845517c113d0a3d9a55dbfb98
--- /dev/null
+++ b/test/mock/services/categoriesFormations.mock.service.ts
@@ -0,0 +1,424 @@
+export class CategoriesFormationsServiceMock {
+  create() {
+    return {
+      modules: [
+        {
+          id: 'tst',
+          text: 'Test',
+        },
+        {
+          id: 'antst',
+          text: 'Another test',
+        },
+      ],
+      name: 'test',
+      id: 'test',
+      __v: 0,
+    };
+  }
+
+  findAll() {
+    return [
+      {
+        _id: '5fbb934180a5c257dc0161f6',
+        modules: [
+          {
+            id: '84',
+            display_id: '84',
+            display_name: 'Modules APTIC - n°84',
+            digest: 'Pôle Emploi : faire ses déclarations en ligne',
+            text: 'Pôle Emploi : faire ses déclarations en ligne',
+          },
+          {
+            id: '85',
+            display_id: '85',
+            display_name: 'Modules APTIC - n°85',
+            digest: 'Déclarer ses revenus en ligne et découvertes des services proposés',
+            text: 'Déclarer ses revenus en ligne et découvertes des services proposés',
+          },
+          {
+            id: '86',
+            display_id: '86',
+            display_name: 'Modules APTIC - n°86',
+            digest: 'Accéder à ses droits sociaux et les gérer en ligne (RSA…)',
+            text: 'Accéder à ses droits sociaux et les gérer en ligne (RSA…)',
+          },
+          {
+            id: '87',
+            display_id: '87',
+            display_name: 'Modules APTIC - n°87',
+            digest: 'Ouvrir et gérer son dossier de retraite (CNAF/CARSAT) en ligne',
+            text: 'Ouvrir et gérer son dossier de retraite (CNAF/CARSAT) en ligne',
+          },
+          {
+            id: '88',
+            display_id: '88',
+            display_name: 'Modules APTIC - n°88',
+            digest: "Gérer son abonnement et ses factures d'électricité/gaz en ligne",
+            text: "Gérer son abonnement et ses factures d'électricité/gaz en ligne",
+          },
+          {
+            id: '89',
+            display_id: '89',
+            display_name: 'Modules APTIC - n°89',
+            digest: "Gérer ses droits d'assuré social en ligne/sur internet",
+            text: "Gérer ses droits d'assuré social en ligne/sur internet",
+          },
+          {
+            id: '93',
+            display_id: '93',
+            display_name: 'Modules APTIC - n°93',
+            digest: 'Plateforme Ameli.fr : la sécurité sociale en ligne',
+            text: 'Plateforme Ameli.fr : la sécurité sociale en ligne',
+          },
+          {
+            id: '95',
+            display_id: '95',
+            display_name: 'Modules APTIC - n°95',
+            digest: 'Plateforme France Connect',
+            text: 'Plateforme France Connect',
+          },
+        ],
+        name: 'Accès aux droits',
+        id: 'accessRight',
+        __v: 0,
+      },
+      {
+        modules: [
+          {
+            id: '260',
+            display_id: '260',
+            display_name: 'Modules APTIC - n°260',
+            digest: 'Maitrise de l’environnement d’un ordinateur (clavier, souris)',
+            text: 'Maitrise de l’environnement d’un ordinateur (clavier, souris)',
+          },
+          {
+            id: '1',
+            display_id: '1',
+            display_name: 'Modules APTIC - n°1',
+            digest: 'Composantes et facettes de l’identité numérique',
+            text: 'Composantes et facettes de l’identité numérique',
+          },
+          {
+            id: '11',
+            display_id: '11',
+            display_name: 'Modules APTIC - n°11',
+            digest: 'Internet : fonctionnement et outils de navigation web',
+            text: 'Internet : fonctionnement et outils de navigation web',
+          },
+          {
+            id: '38',
+            display_id: '38',
+            display_name: 'Modules APTIC - n°38',
+            digest: 'Le smartphone : principes de fonctionnement',
+            text: 'Le smartphone : principes de fonctionnement',
+          },
+          {
+            id: '48',
+            display_id: '48',
+            display_name: 'Modules APTIC - n°48',
+            digest: 'Internet : envoyer, recevoir, gérer ses emails',
+            text: 'Internet : envoyer, recevoir, gérer ses emails',
+          },
+          {
+            id: '74',
+            display_id: '74',
+            display_name: 'Modules APTIC - n°74',
+            digest: 'Smartphones et Tablettes sous Androïd',
+            text: 'Smartphones et Tablettes sous Androïd',
+          },
+          {
+            id: '77',
+            display_id: '77',
+            display_name: 'Modules APTIC - n°77',
+            digest: "Smartphone : Les principaux gestes pour l'écran tactile",
+            text: "Smartphone : Les principaux gestes pour l'écran tactile",
+          },
+        ],
+        name: 'Les compétences de base',
+        id: 'baseSkills',
+        __v: 0,
+      },
+      {
+        modules: [
+          {
+            id: '3',
+            display_id: '3',
+            display_name: 'Modules APTIC - n°3',
+            digest: "Être parent à l'ère numérique: connaître les usages, jouer son rôle de parent",
+            text: "Être parent à l'ère numérique: connaître les usages, jouer son rôle de parent",
+          },
+          {
+            id: '22',
+            display_id: '22',
+            display_name: 'Modules APTIC - n°22',
+            digest: 'Découvrir l’univers des jeux vidéo - 22',
+            text: 'Découvrir l’univers des jeux vidéo',
+          },
+          {
+            id: '82',
+            display_id: '82',
+            display_name: 'Modules APTIC - n°82',
+            digest: 'Suivre la scolarité de son enfant - 82',
+            text: 'Suivre la scolarité de son enfant',
+          },
+          {
+            id: '94',
+            display_id: '94',
+            display_name: 'Modules APTIC - n°94',
+            digest: "Découvrir les services en ligne de l'enfance de votre commune - 94",
+            text: "Découvrir les services en ligne de l'enfance de votre commune",
+          },
+        ],
+        name: 'Aide à la parentalité',
+        id: 'parentingHelp',
+        __v: 0,
+      },
+      {
+        modules: [
+          {
+            id: '6',
+            display_id: '6',
+            display_name: 'Modules APTIC - n°6',
+            digest: "Utiliser les réseaux sociaux pour sa recherche d'emploi",
+            text: "Utiliser les réseaux sociaux pour sa recherche d'emploi",
+          },
+          {
+            id: '20',
+            display_id: '20',
+            display_name: 'Modules APTIC - n°20',
+            digest: 'Panorama des plateformes de recherche d’emploi',
+            text: 'Panorama des plateformes de recherche d’emploi',
+          },
+          {
+            id: '66',
+            display_id: '66',
+            display_name: 'Modules APTIC - n°66',
+            digest: 'Traitement de texte : découverte',
+            text: 'Traitement de texte : découverte',
+          },
+          {
+            id: '67',
+            display_id: '67',
+            display_name: 'Modules APTIC - n°67',
+            digest: 'Traitement de texte : utilisation de base',
+            text: 'Traitement de texte : utilisation de base',
+          },
+          {
+            id: '68',
+            display_id: '68',
+            display_name: 'Modules APTIC - n°68',
+            digest: 'Traitement de texte : utilisation avancée',
+            text: 'Traitement de texte : utilisation avancée',
+          },
+          {
+            id: '69',
+            display_id: '69',
+            display_name: 'Modules APTIC - n°69',
+            digest: 'Tableur : découverte',
+            text: 'Tableur : découverte',
+          },
+          {
+            id: '124',
+            display_id: '124',
+            display_name: 'Modules APTIC - n°124',
+            digest: 'Réalisation CV',
+            text: 'Réalisation CV',
+          },
+          {
+            id: '125',
+            display_id: '125',
+            display_name: 'Modules APTIC - n°125',
+            digest: 'Diffuser son CV en ligne',
+            text: 'Diffuser son CV en ligne',
+          },
+          {
+            id: '127',
+            display_id: '127',
+            display_name: 'Modules APTIC - n°127',
+            digest: "Organiser sa recherche d'emploi",
+            text: "Organiser sa recherche d'emploi",
+          },
+        ],
+        name: 'Insertion sociale et professionnelle',
+        id: 'socialAndProfessional',
+        __v: 0,
+      },
+      {
+        modules: [
+          {
+            id: '6',
+            display_id: '6',
+            display_name: 'Modules APTIC - n°6',
+            digest: "Utiliser les réseaux sociaux pour sa recherche d'emploi",
+            text: "Utiliser les réseaux sociaux pour sa recherche d'emploi",
+          },
+          {
+            id: '20',
+            display_id: '20',
+            display_name: 'Modules APTIC - n°20',
+            digest: 'Panorama des plateformes de recherche d’emploi',
+            text: 'Panorama des plateformes de recherche d’emploi',
+          },
+          {
+            id: '66',
+            display_id: '66',
+            display_name: 'Modules APTIC - n°66',
+            digest: 'Traitement de texte : découverte',
+            text: 'Traitement de texte : découverte',
+          },
+          {
+            id: '67',
+            display_id: '67',
+            display_name: 'Modules APTIC - n°67',
+            digest: 'Traitement de texte : utilisation de base',
+            text: 'Traitement de texte : utilisation de base',
+          },
+          {
+            id: '68',
+            display_id: '68',
+            display_name: 'Modules APTIC - n°68',
+            digest: 'Traitement de texte : utilisation avancée',
+            text: 'Traitement de texte : utilisation avancée',
+          },
+          {
+            id: '69',
+            display_id: '69',
+            display_name: 'Modules APTIC - n°69',
+            digest: 'Tableur : découverte',
+            text: 'Tableur : découverte',
+          },
+          {
+            id: '124',
+            display_id: '124',
+            display_name: 'Modules APTIC - n°124',
+            digest: 'Réalisation CV',
+            text: 'Réalisation CV',
+          },
+          {
+            id: '125',
+            display_id: '125',
+            display_name: 'Modules APTIC - n°125',
+            digest: 'Diffuser son CV en ligne',
+            text: 'Diffuser son CV en ligne',
+          },
+          {
+            id: '127',
+            display_id: '127',
+            display_name: 'Modules APTIC - n°127',
+            digest: "Organiser sa recherche d'emploi",
+            text: "Organiser sa recherche d'emploi",
+          },
+        ],
+        name: 'Insertion sociale et professionnelle',
+        id: 'socialAndProfessional',
+        __v: 0,
+      },
+      {
+        modules: [
+          {
+            id: '2',
+            display_id: '2',
+            display_name: 'Modules APTIC - n°2',
+            digest: 'Les conduites à risques et les bons usages du numérique - 02',
+            text: 'Les conduites à risques et les bons usages du numérique',
+          },
+          {
+            id: '5',
+            display_id: '5',
+            display_name: 'Modules APTIC - n°5',
+            digest: 'Découvrir les réseaux sociaux : définition, fonctionnement - 05',
+            text: 'Découvrir les réseaux sociaux : définition, fonctionnement',
+          },
+          {
+            id: '9',
+            display_id: '9',
+            display_name: 'Modules APTIC - n°9',
+            digest: 'Fablab : charte, valeurs et panorama des outils numériques - 09',
+            text: 'Fablab : charte, valeurs et panorama des outils numériques',
+          },
+          {
+            id: '28',
+            display_id: '28',
+            display_name: 'Modules APTIC - n°28',
+            digest: 'Les paiements en ligne - 28',
+            text: 'Les paiements en ligne',
+          },
+          {
+            id: '34',
+            display_id: '34',
+            display_name: 'Modules APTIC - n°34',
+            digest: 'Internet : comprendre les principes de fonctionnement - 35',
+            text: 'Internet : comprendre les principes de fonctionnement',
+          },
+          {
+            id: '39',
+            display_id: '39',
+            display_name: 'Modules APTIC - n°39',
+            digest: 'Techniques de vérification de l’information - 39',
+            text: 'Techniques de vérification de l’information',
+          },
+          {
+            id: '42',
+            display_id: '42',
+            display_name: 'Modules APTIC - n°42',
+            digest: 'Créer et paramétrer un compte Google - 42',
+            text: 'Créer et paramétrer un compte Google',
+          },
+          {
+            id: '51',
+            display_id: '51',
+            display_name: 'Modules APTIC - n°51',
+            digest: 'Gérer ses données : sauvegarde en ligne (dans le cloud) - 51',
+            text: 'Gérer ses données : sauvegarde en ligne (dans le cloud)',
+          },
+          {
+            id: '52',
+            display_id: '52',
+            display_name: 'Modules APTIC - n°52',
+            digest: 'Gérer ses données : sauvegardes locales (disques durs externes, clé USB) - 52',
+            text: 'Gérer ses données : sauvegardes locales (disques durs externes, clé USB)',
+          },
+          {
+            id: '54',
+            display_id: '54',
+            display_name: 'Modules APTIC - n°54',
+            digest: 'Classer, gérer et partager ses photos - 54',
+            text: 'Classer, gérer et partager ses photos',
+          },
+          {
+            id: '65',
+            display_id: '65',
+            display_name: 'Modules APTIC - n°65',
+            digest: 'Skype et autres outils de visioconférence - 65',
+            text: 'Skype et autres outils de visioconférence',
+          },
+          {
+            id: '96',
+            display_id: '96',
+            display_name: 'Modules APTIC - n°96',
+            digest: 'Connaitre et gérer son identité numérique - 96',
+            text: 'Connaitre et gérer son identité numérique',
+          },
+          {
+            id: '97',
+            display_id: '97',
+            display_name: 'Modules APTIC - n°97',
+            digest: 'Nettoyer son identité numérique - 97',
+            text: 'Nettoyer son identité numérique',
+          },
+          {
+            id: '98',
+            display_id: '98',
+            display_name: 'Modules APTIC - n°98',
+            digest: 'Effacer ses traces sur le web, protéger ses données personnelles - 98',
+            text: 'Effacer ses traces sur le web, protéger ses données personnelles',
+          },
+        ],
+        name: 'Culture et sécurité numérique',
+        id: 'digitalCultureSecurity',
+        __v: 0,
+      },
+    ];
+  }
+}
diff --git a/test/mock/services/categoriesOthers.mock.service.ts b/test/mock/services/categoriesOthers.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7b952c68df7b252f1ef99110583fdba025724bce
--- /dev/null
+++ b/test/mock/services/categoriesOthers.mock.service.ts
@@ -0,0 +1,49 @@
+export class CategoriesOthersServiceMock {
+  create() {
+    return {
+      modules: [
+        {
+          id: 'tst',
+          text: 'Test',
+        },
+      ],
+      name: 'test',
+      id: 'test',
+      __v: 0,
+    };
+  }
+
+  findAll() {
+    return [
+      {
+        modules: [
+          {
+            id: "Personnes en situation d'illetrisme",
+            text: "Personnes en situation d'illetrisme",
+          },
+          {
+            id: 'Langue étrangère (anglais)',
+            text: 'Langue étrangère (anglais)',
+          },
+          {
+            id: 'Langues étrangères (autres)',
+            text: 'Langues étrangères (autres)',
+          },
+          {
+            id: 'Surdité',
+            text: 'Surdité',
+          },
+          {
+            id: 'Déficience visuelle',
+            text: 'Déficience visuelle',
+          },
+          {
+            id: 'Handicap moteur',
+            text: 'Handicap moteur',
+          },
+        ],
+        name: 'Accompagnement des publics spécifique',
+      },
+    ];
+  }
+}
diff --git a/test/mock/services/http.mock.service.ts b/test/mock/services/http.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2295a70104969be97a1218df189c2e5a1f145d8e
--- /dev/null
+++ b/test/mock/services/http.mock.service.ts
@@ -0,0 +1,8 @@
+import { AxiosRequestConfig, AxiosResponse } from 'axios';
+import { Observable } from 'rxjs';
+
+export class HttpServiceMock {
+  get<T = any>(url: string, config?: AxiosRequestConfig): Observable<AxiosResponse<T>> {
+    return;
+  }
+}
diff --git a/test/mock/services/jwt.mock.service.ts b/test/mock/services/jwt.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a5c81b3344c930122a768d11f9db8ca49c55f67c
--- /dev/null
+++ b/test/mock/services/jwt.mock.service.ts
@@ -0,0 +1,5 @@
+export class JwtServiceMock {
+  sign() {
+    return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBhdWxpbmUuZHVwb250QG1paS5jb20iLCJyb2xlIjowLCJpYXQiOjE2MjAwNDg5MDYsImV4cCI6MTYyMDEzNTMwNn0.jbLazQNJzU_X9Yp1S7XH1rYD5W7yyd1pdGebmkyTMB4';
+  }
+}
diff --git a/test/mock/services/newsletter.mock.service.ts b/test/mock/services/newsletter.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d7b4d640c0d9cd298961fe88a660aeebbb158b1d
--- /dev/null
+++ b/test/mock/services/newsletter.mock.service.ts
@@ -0,0 +1,58 @@
+import { HttpException, HttpStatus } from '@nestjs/common';
+
+export class NewsletterServiceMock {
+  findAll() {
+    return [
+      {
+        _id: '607063e22b016303ad97f367',
+        email: 'xavier@mii.fr',
+        createdAt: '2021-04-09T10:14:01.939Z',
+        updatedAt: '2021-04-09T10:14:01.939Z',
+      },
+      {
+        _id: '607063e22b016303ad97f368',
+        email: 'paula.dubois@mii.com',
+        createdAt: '2021-04-09T10:14:01.939Z',
+        updatedAt: '2021-04-09T10:14:01.939Z',
+      },
+      {
+        _id: '607063e22b016303ad97f369',
+        email: 'a@a.com',
+        createdAt: '2021-04-09T10:14:01.939Z',
+        updatedAt: '2021-04-09T10:14:01.939Z',
+      },
+    ];
+  }
+
+  searchNewsletterSubscription(search: string) {
+    if (search === 'a@a.com') {
+      return [
+        {
+          _id: '607063e22b016303ad97f369',
+          email: 'a@a.com',
+          createdAt: '2021-04-09T10:14:01.939Z',
+          updatedAt: '2021-04-09T10:14:01.939Z',
+        },
+      ];
+    }
+    return [];
+  }
+
+  countNewsletterSubscriptions() {
+    return new Promise((resolve) => {
+      resolve(246);
+    });
+  }
+
+  deleteOneEmail(email: string) {
+    if (email === 'test@test.com') {
+      throw new HttpException('Invalid  email', HttpStatus.BAD_REQUEST);
+    }
+    return {
+      _id: '607063e22b016303ad97f397',
+      email: 'a@a.com',
+      createdAt: '2021-04-09T10:14:01.939Z',
+      updatedAt: '2021-04-09T10:14:01.939Z',
+    };
+  }
+}
diff --git a/test/mock/services/structures.mock.service.ts b/test/mock/services/structures.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..276dfd05e723b0aaf0e74b3d991818fb55f7f007
--- /dev/null
+++ b/test/mock/services/structures.mock.service.ts
@@ -0,0 +1,332 @@
+import { HttpException, HttpStatus } from '@nestjs/common';
+
+export class StructuresServiceMock {
+  findOne(id) {
+    if (id === '6093ba0e2ab5775cfc01ed3e') {
+      return {
+        _id: '6093ba0e2ab5775cfc01ed3e',
+        coord: [4.8498155, 45.7514817],
+        equipmentsAndServices: ['wifiEnAccesLibre'],
+        digitalCultureSecurity: [],
+        parentingHelp: [],
+        socialAndProfessional: [],
+        accessRight: [],
+        baseSkills: [],
+        proceduresAccompaniment: [],
+        publicsAccompaniment: [],
+        publics: ['adultes'],
+        labelsQualifications: [],
+        accessModality: ['telephoneVisio'],
+        structureType: null,
+        structureName: 'a',
+        description: null,
+        lockdownActivity: null,
+        address: {
+          numero: null,
+          street: 'Rue Alphonse Daudet',
+          commune: 'Lyon 7ème Arrondissement',
+        },
+        contactMail: '',
+        contactPhone: '',
+        website: '',
+        facebook: null,
+        twitter: null,
+        instagram: null,
+        linkedin: null,
+        hours: {
+          monday: {
+            open: false,
+            time: [],
+          },
+          tuesday: {
+            open: false,
+            time: [],
+          },
+          wednesday: {
+            open: false,
+            time: [],
+          },
+          thursday: {
+            open: false,
+            time: [],
+          },
+          friday: {
+            open: false,
+            time: [],
+          },
+          saturday: {
+            open: false,
+            time: [],
+          },
+          sunday: {
+            open: false,
+            time: [],
+          },
+        },
+        pmrAccess: false,
+        exceptionalClosures: null,
+        otherDescription: null,
+        nbComputers: 1,
+        nbPrinters: 1,
+        nbTablets: 1,
+        nbNumericTerminal: 1,
+        nbScanners: 1,
+        freeWorkShop: false,
+        accountVerified: true,
+        createdAt: '2021-05-06T09:42:38.000Z',
+        updatedAt: '2021-05-06T09:42:50.000Z',
+        __v: 0,
+      };
+    }
+
+    if (id === '6903ba0e2ab5775cfc01ed4d') {
+      return {
+        _id: '6903ba0e2ab5775cfc01ed4d',
+        structureType: null,
+        digitalCultureSecurity: ['2', '5', '9', '28', '34', '39', '42', '51', '52', '54', '65', '96', '97', '98'],
+        parentingHelp: ['3', '22', '82', '94'],
+        socialAndProfessional: ['6', '20', '66', '67', '68', '69', '124', '125', '127'],
+        accessRight: ['84', '85', '86', '87', '88', '89', '93', '95'],
+        baseSkills: ['260', '1', '11', '38', '48', '74', '77'],
+        proceduresAccompaniment: ['cpam', 'impots', 'carsat', 'poleEmploi'],
+        publics: ['toutPublic'],
+        labelsQualifications: ['passNumerique', 'espacePublicNumeriqueepn'],
+        accessModality: ['accesLibre', 'telephoneVisio', 'surRdv'],
+        freeWorkShop: false,
+        createdAt: '2020-11-16T09:30:00.000Z',
+        updatedAt: '2021-04-12T08:48:00.000Z',
+        structureName: "L'Atelier Numérique",
+        description:
+          "L'Atelier Numérique est l'Espace Public Numérique des Centres Sociaux de Rillieux-la-Pape, ayant pour mission la médiation numérique pour toutes et tous.",
+        lockdownActivity:
+          'accesLibres, permanences numériques téléphoniques, cours et ateliers à distance, formations professionnelles.',
+        contactPhone: '',
+        contactMail: '',
+        website: '',
+        facebook: null,
+        twitter: null,
+        instagram: null,
+        pmrAccess: true,
+        exceptionalClosures: '',
+        jaccompagneLesUsagersDansLeursDemarchesEnLigne: true,
+        publicsAccompaniment: [],
+        autresAccompagnements: '',
+        equipmentsAndServices: ['ordinateurs', 'tablettes'],
+        nbComputers: 16,
+        nbPrinters: 1,
+        nbTablets: 1,
+        nbNumericTerminal: 1,
+        hours: {
+          monday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+              {
+                closing: '17:00',
+                opening: '13:30',
+              },
+            ],
+          },
+          tuesday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+              {
+                closing: '17:00',
+                opening: '13:30',
+              },
+            ],
+          },
+          wednesday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+              {
+                closing: '17:00',
+                opening: '13:30',
+              },
+            ],
+          },
+          thursday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+              {
+                closing: '17:00',
+                opening: '13:30',
+              },
+            ],
+          },
+          friday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+            ],
+          },
+          saturday: {
+            open: false,
+            time: [],
+          },
+          sunday: {
+            open: false,
+            time: [],
+          },
+        },
+        __v: 0,
+        address: {
+          numero: '30 bis',
+          street: 'Avenue Leclerc',
+          commune: 'Rillieux-la-Pape',
+        },
+        coord: [4.9036773, 45.8142196],
+        accountVerified: true,
+        linkedin: null,
+        nbScanners: 1,
+        otherDescription: null,
+      };
+    }
+
+    return null;
+  }
+
+  deleteOne(id) {
+    if (id === '6903ba0e2ab5775cfc01ed4d') {
+      return {
+        _id: '6903ba0e2ab5775cfc01ed4d',
+        structureType: null,
+        digitalCultureSecurity: ['2', '5', '9', '28', '34', '39', '42', '51', '52', '54', '65', '96', '97', '98'],
+        parentingHelp: ['3', '22', '82', '94'],
+        socialAndProfessional: ['6', '20', '66', '67', '68', '69', '124', '125', '127'],
+        accessRight: ['84', '85', '86', '87', '88', '89', '93', '95'],
+        baseSkills: ['260', '1', '11', '38', '48', '74', '77'],
+        proceduresAccompaniment: ['cpam', 'impots', 'carsat', 'poleEmploi'],
+        publics: ['toutPublic'],
+        labelsQualifications: ['passNumerique', 'espacePublicNumeriqueepn'],
+        accessModality: ['accesLibre', 'telephoneVisio', 'surRdv'],
+        freeWorkShop: false,
+        createdAt: '2020-11-16T09:30:00.000Z',
+        updatedAt: '2021-04-12T08:48:00.000Z',
+        structureName: "L'Atelier Numérique",
+        description:
+          "L'Atelier Numérique est l'Espace Public Numérique des Centres Sociaux de Rillieux-la-Pape, ayant pour mission la médiation numérique pour toutes et tous.",
+        lockdownActivity:
+          'accesLibres, permanences numériques téléphoniques, cours et ateliers à distance, formations professionnelles.',
+        contactPhone: '',
+        contactMail: '',
+        website: '',
+        facebook: null,
+        twitter: null,
+        instagram: null,
+        pmrAccess: true,
+        exceptionalClosures: '',
+        jaccompagneLesUsagersDansLeursDemarchesEnLigne: true,
+        publicsAccompaniment: [],
+        autresAccompagnements: '',
+        equipmentsAndServices: ['ordinateurs', 'tablettes'],
+        nbComputers: 16,
+        nbPrinters: 1,
+        nbTablets: 1,
+        nbNumericTerminal: 1,
+        hours: {
+          monday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+              {
+                closing: '17:00',
+                opening: '13:30',
+              },
+            ],
+          },
+          tuesday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+              {
+                closing: '17:00',
+                opening: '13:30',
+              },
+            ],
+          },
+          wednesday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+              {
+                closing: '17:00',
+                opening: '13:30',
+              },
+            ],
+          },
+          thursday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+              {
+                closing: '17:00',
+                opening: '13:30',
+              },
+            ],
+          },
+          friday: {
+            open: true,
+            time: [
+              {
+                closing: '12:30',
+                opening: '9:00',
+              },
+            ],
+          },
+          saturday: {
+            open: false,
+            time: [],
+          },
+          sunday: {
+            open: false,
+            time: [],
+          },
+        },
+        __v: 0,
+        address: {
+          numero: '30 bis',
+          street: 'Avenue Leclerc',
+          commune: 'Rillieux-la-Pape',
+        },
+        coord: [4.9036773, 45.8142196],
+        accountVerified: true,
+        linkedin: null,
+        nbScanners: 1,
+        otherDescription: null,
+      };
+    }
+
+    throw new HttpException('Invalid structure id', HttpStatus.BAD_REQUEST);
+  }
+}
diff --git a/test/mock/services/tempUser.mock.service.ts b/test/mock/services/tempUser.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8769fa94f3b52596b1a366312475df0f08b05830
--- /dev/null
+++ b/test/mock/services/tempUser.mock.service.ts
@@ -0,0 +1 @@
+export class TempUserServiceMock {}
diff --git a/test/mock/services/user.mock.service.ts b/test/mock/services/user.mock.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1b44c701fded2d30f041bf14f3ce1c6624f77018
--- /dev/null
+++ b/test/mock/services/user.mock.service.ts
@@ -0,0 +1,190 @@
+import { HttpException, HttpStatus } from '@nestjs/common';
+import { PendingStructureDto } from '../../../src/admin/dto/pending-structure.dto';
+import { LoginDto } from '../../../src/auth/login-dto';
+
+export class UsersServiceMock {
+  findOne(mail: string, passwordQuery?: boolean) {
+    if (mail === 'pauline.dupont@mii.com') {
+      return {
+        _id: 'tsfsf6296',
+        validationToken:
+          'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+        emailVerified: true,
+        email: 'pauline.dupont@mii.com',
+        password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
+        role: 0,
+        name: 'DUPONT',
+        surname: 'Pauline',
+      };
+    }
+    if (mail === 'jacques.dupont@mii.com') {
+      if (passwordQuery) {
+        return {
+          _id: 'tsfsf6296',
+          validationToken:
+            'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+          emailVerified: false,
+          email: 'jacques.dupont@mii.com',
+          password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
+          role: 0,
+        };
+      }
+      return {
+        _id: 'tsfsf6296',
+        validationToken:
+          'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+        emailVerified: false,
+        email: 'jacques.dupont@mii.com',
+        role: 0,
+      };
+    }
+    return null;
+  }
+
+  findByLogin({ email, password }: LoginDto) {
+    const user = this.findOne(email, true);
+
+    if (!user) {
+      throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+    }
+
+    let areEqual = true;
+    if (password === '1') {
+      areEqual = false;
+    }
+
+    if (!areEqual) {
+      throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
+    }
+
+    return user;
+  }
+
+  getPendingStructures() {
+    return [
+      {
+        structureId: '6093ba0e2ab5775cfc01ed3e',
+        structureName: 'a',
+        userEmail: 'paula.dubois@mii.com',
+      },
+      {
+        structureId: '6903ba0e2ab5775cfc01ed4d',
+        structureName: "L'Atelier Numérique",
+        userEmail: 'jacques.dupont@mii.com',
+      },
+    ];
+  }
+
+  validatePendingStructure(): PendingStructureDto[] {
+    return this.getPendingStructures();
+  }
+
+  deleteOneId(id: string) {
+    if (id === 'tsfsf6296') {
+      return {
+        _id: 'tsfsf6296',
+        validationToken:
+          'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+        emailVerified: true,
+        email: 'pauline.dupont@mii.com',
+        password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
+        role: 0,
+        name: 'DUPONT',
+        surname: 'Pauline',
+        structuresLink: ['abcdefgh', '18sfqfq'],
+      };
+    }
+    throw new HttpException('Invalid user id', HttpStatus.BAD_REQUEST);
+  }
+
+  isStructureClaimed(id: string) {
+    if (id === 'abcdefgh') {
+      return new Promise((resolve) => {
+        resolve({
+          _id: 'abcdefgh',
+          validationToken:
+            'cf1c74c22cedb6b575945098db42d2f493fb759c9142c6aff7980f252886f36ee086574ee99a06bc99119079257116c959c8ec870949cebdef2b293666dbca42',
+          emailVerified: true,
+          email: 'pauline.dupont@mii.com',
+          password: '$2a$12$vLQjJ9zAWyUwiFLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
+          role: 0,
+          name: 'DUPONT',
+          surname: 'Pauline',
+          structuresLink: ['abcdefgh', '18sfqfq'],
+        });
+      });
+    }
+    return new Promise((resolve) => {
+      resolve(null);
+    });
+  }
+
+  searchUsers(searchString: string) {
+    if (searchString === 'a@a.com') {
+      return [
+        {
+          structureOutdatedMailSent: [],
+          pendingStructuresLink: ['6001a48e16b08100062e4180'],
+          structuresLink: [],
+          newEmail: null,
+          changeEmailToken: null,
+          role: 0,
+          resetPasswordToken: null,
+          validationToken:
+            'b2b6caca1d38ca26d203b5f12b0d925df2928fab8ee7ccf9bbe78802ffa625f5abce825783bc62d0b11be5a90132cf5045a9a7776f01694c63b60bf64b0f680f',
+          emailVerified: false,
+          _id: '6036721022462b001334c4bb',
+          email: 'a@a.com',
+          name: 'Xavier',
+          surname: 'NIEL',
+          phone: '06 11 11 11 11',
+          password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
+          __v: 1,
+        },
+      ];
+    }
+    return [];
+  }
+
+  findAll() {
+    return [
+      {
+        structureOutdatedMailSent: [],
+        pendingStructuresLink: ['6001a48e16b08100062e4180'],
+        structuresLink: [],
+        newEmail: null,
+        changeEmailToken: null,
+        role: 0,
+        resetPasswordToken: null,
+        validationToken:
+          'b2b6caca1d38ca26d203b5f12b0d925df2928fab8ee7ccf9bbe78802ffa625f5abce825783bc62d0b11be5a90132cf5045a9a7776f01694c63b60bf64b0f680f',
+        emailVerified: false,
+        _id: '6036721022462b001334c4bb',
+        email: 'a@a.com',
+        name: 'Xavier',
+        surname: 'NIEL',
+        phone: '06 11 11 11 11',
+        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.XzrlgPBhw.2GWrjog/yuEjIaZnQwmZu',
+        __v: 1,
+      },
+      {
+        structureOutdatedMailSent: [],
+        pendingStructuresLink: ['6001a3c216b08100062e4169', '601d6aa2c94cf895c4e23860'],
+        structuresLink: ['6037aa17d8189c0014f62421'],
+        newEmail: null,
+        changeEmailToken: null,
+        role: 0,
+        resetPasswordToken: null,
+        validationToken: null,
+        emailVerified: true,
+        _id: '6037aa16d8189c0014f6241f',
+        email: 'ptitduf@mii.com',
+        name: 'hjhj',
+        surname: 'hh',
+        phone: '05 79 87 65 78',
+        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.3KdcYb.cLqZzkX2rstcpc2QTgbJ0FlC',
+        __v: 3,
+      },
+    ];
+  }
+}