diff --git a/.gitignore b/.gitignore
index d250e2019542fe420edbfcd807e80ca8094e72f2..72f4c9edb56add14c46828468683b3d2a31b7559 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,4 +58,4 @@ desktop.ini
 
 # binary file
 puissance4
-test_board
\ No newline at end of file
+test_puissance4
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 15bb36541ef4f40367b0dd50e969fc0c3dc7a3cb..4a0093ae7c56991ef88deb8184369a88bf92af38 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,33 +12,27 @@ build:
     - echo "Compiling project..."
     - make clean
     - make puissance4
+    - make test_puissance4
   artifacts:
     paths:
       - puissance4
+      - test_puissance4
 
-test_2players:
+testbest:
   stage: test
   script:
-    - echo "Running 2players tests..."
-    - make -C testbed/2players
+    - echo "Running testbest..."
+    - make tests
+  dependencies:
+    - build
 
-test_rand_ai:
+test_unit:
   stage: test
   script:
-    - echo "Running rand_ai tests..."
-    - make -C testbed/rand_ai
-
-test_smart_ai:
-  stage: test
-  script:
-    - echo "Running smart_ai tests..."
-    - make -C testbed/smart_ai
-
-test_board:
-  stage: test
-  script:
-    - echo "Running board tests..."
-    - make tests_board
+    - echo "Running unit tests..."
+    - make run_unit_tests
+  dependencies:
+    - build
 
 clean:
   stage: clean
diff --git a/Makefile b/Makefile
index 727325a4aba71a99ddd63bece287b12c0d560cc7..50b3ac0398a1148930f1d55e13b23766b78e9ee4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,51 +1,50 @@
-CC:=gcc
-CFLAGS  = -g -Wall -Wextra -Wpedantic
-LDLIBS  = -lm
-LDFLAGS = -fsanitize=address -fsanitize=leak
+CC := gcc
+CFLAGS := -g -Wall -Wextra -Wpedantic
+LDLIBS := -lm
+LDFLAGS := -fsanitize=address -fsanitize=leak
 
 puissance4: puissance4.o src/board.o src/computer.o src/game.o
 	$(CC) $(CFLAGS) $^ -o $@ $(LDLIBS) $(LDFLAGS)
 
-board.o: src/board.h
-game.o: src/game.h src/computer.h
-computer.o: src/computer.h src/board.h
-puissance4.o: puissance4.h src/game.h src/board.h
+test_puissance4: src/test_puissance4.o src/test_board.o src/test_game.o src/test_computer.o src/board.o src/computer.o src/game.o
+	$(CC) $(CFLAGS) $^ -o $@ $(LDLIBS) $(LDFLAGS)
 
-clean:
-	@echo "this rule must clean everything up (including candidate files in testbed)"
-	$(MAKE) -C testbed clean
-	$(RM) -rf puissance4 *.o tests_game tests_board tests_computer
+puissance4.o: puissance4.c puissance4.h src/game.h src/board.h
+	$(CC) $(CFLAGS) -c $< -o $@
 
-run: puissance4 
-	./$< 3 5 6
+src/board.o: src/board.c src/board.h
+	$(CC) $(CFLAGS) -c $< -o $@
 
-tests: puissance4
-	$(MAKE) -C testbed
+src/computer.o: src/computer.c src/computer.h src/board.h
+	$(CC) $(CFLAGS) -c $< -o $@
+
+src/game.o: src/game.c src/game.h src/computer.h src/board.h
+	$(CC) $(CFLAGS) -c $< -o $@
 
-tests_2players: puissance4
-	$(MAKE) -C testbed/2players
+src/test_puissance4.o: src/test_puissance4.c src/test_puissance4.h src/test_board.h src/test_game.h src/test_computer.h
+	$(CC) $(CFLAGS) -c $< -o $@
 
-tests_rand_ai: puissance4
-	$(MAKE) -C testbed/rand_ai
+src/test_board.o: src/test_board.c src/test_board.h src/board.h
+	$(CC) $(CFLAGS) -c $< -o $@
 
-tests_smart_ai: puissance4
-	$(MAKE) -C testbed/smart_ai
+src/test_game.o: src/test_game.c src/test_game.h src/board.h src/computer.h
+	$(CC) $(CFLAGS) -c $< -o $@
 
-tests_game: src/test_game.o src/board.o src/game.o src/computer.o
-	$(CC) $(CFLAGS) $^ -o tests_game $(LDLIBS) $(LDFLAGS)
-	./tests_game
+src/test_computer.o: src/test_computer.c src/test_computer.h src/board.h src/computer.h
+	$(CC) $(CFLAGS) -c $< -o $@
 
-tests_board: src/test_board.o src/board.o
-	$(CC) $(CFLAGS) $^ -o tests_board $(LDLIBS) $(LDFLAGS)
-	./tests_board
+clean:
+	$(MAKE) -C testbed clean
+	$(RM) -rf puissance4 test_puissance4 *.o src/*.o
+
+run: puissance4
+	./$< 3 5 6
 
-tests_computer: src/test_computer.o src/computer.o src/board.o src/game.o
-	$(CC) $(CFLAGS) $^ -o tests_computer $(LDLIBS) $(LDFLAGS)
-	./tests_computer
+run_unit_tests: test_puissance4
+	./$<
 
-run_tests: tests_game tests_board tests_computer
-	@echo "Running all tests..."
-	./tests_game
-	./tests_board
-	./tests_computer
-	@echo "All tests executed successfully."
\ No newline at end of file
+run_all_tests: test_puissance4 tests
+	./$<
+
+tests: puissance4
+	$(MAKE) -C testbed
diff --git a/src/test_board.c b/src/test_board.c
index 5e351ae08b21cc5c71f69632abf2f51983e9ac33..5ef77225edb1abb15554c4bef7fd9cee0c407999 100644
--- a/src/test_board.c
+++ b/src/test_board.c
@@ -1,23 +1,4 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-#include "board.h"
-
-#define GREEN "\033[1;32m"
-#define RED "\033[1;31m"
-#define RESET "\033[0m"
-
-void test_destroy_board();
-void test_fill_board();
-void test_create_board();
-
-int main() {
-    test_create_board();
-    test_fill_board();
-    test_destroy_board();
-    return EXIT_SUCCESS;
-}
+#include "test_board.h"
 
 void test_destroy_board() {
     int rows = 6;
@@ -29,7 +10,7 @@ void test_destroy_board() {
     }
 
     fill_board(rows, cols, board);
-    assert(destroy_board(rows, board) == EXIT_SUCCESS);
+    assert(destroy_board(rows, board) == EXIT_SUCCESS && RED "test_destroy_board: FAILED\n" RESET);
     printf(GREEN "test_destroy_board: PASSED\n" RESET);
 }
 
@@ -37,10 +18,10 @@ void test_create_board() {
     int rows = 6;
     int cols = 7;
     int** board = create_board(rows, cols);
-    assert(board != NULL && "Board creation failed");
+    assert(board != NULL && RED "Board creation failed\n" RESET);
 
     for (int i = 0; i < rows; i++) {
-        assert(board[i] != NULL && "Row allocation failed");
+        assert(board[i] != NULL && RED "Row allocation failed\n" RESET);
     }
 
     printf(GREEN "test_create_board: PASSED\n" RESET);
@@ -51,13 +32,13 @@ void test_fill_board() {
     int rows = 6;
     int cols = 7;
     int** board = create_board(rows, cols);
-    assert(board != NULL && "Board creation failed");
+    assert(board != NULL && RED "Board creation failed\n" RESET);
 
     fill_board(rows, cols, board);
 
     for (int i = 0; i < rows; i++) {
         for (int j = 0; j < cols; j++) {
-            assert(board[i][j] == EMPTY);
+            assert(board[i][j] == EMPTY && RED "test_fill_board: FAILED at board[%d][%d]\n" RESET);
         }
     }
 
diff --git a/src/test_board.h b/src/test_board.h
new file mode 100644
index 0000000000000000000000000000000000000000..ec5ccd3dd7841478daae904f3fcd10ad07053f88
--- /dev/null
+++ b/src/test_board.h
@@ -0,0 +1,11 @@
+#ifndef _TEST_BOARD_H_
+#define _TEST_BOARD_H_
+
+#include "board.h"
+#include "test_puissance4.h"
+
+void test_destroy_board();
+void test_fill_board();
+void test_create_board();
+
+#endif
diff --git a/src/test_computer.c b/src/test_computer.c
index 63a111d38d9fb2c1342d6db62edc0a1ea707f0aa..35e71226c33d0581d9a251cfe044d46293ee87b0 100644
--- a/src/test_computer.c
+++ b/src/test_computer.c
@@ -1,47 +1,20 @@
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "computer.h"
-#include "game.h"
-
-int** setup_board(int rows, int cols, int default_value) {
-    int** board = create_board(rows, cols);
-    if (board == NULL) {
-        fprintf(stderr, "Error: Unable to allocate memory for the board.\n");
-        exit(EXIT_FAILURE);
-    }
-
-    for (int i = 0; i < rows; i++) {
-        for (int j = 0; j < cols; j++) {
-            board[i][j] = default_value;
-        }
-    }
-
-    return board;
-}
-
-void reset_board(int rows, int cols, int** board) {
-    for (int i = 0; i < rows; i++) {
-        for (int j = 0; j < cols; j++) {
-            board[i][j] = EMPTY;
-        }
-    }
-}
+#include "test_computer.h"
 
 void test_is_column_full() {
     int rows = 6, cols = 7;
     int** board = setup_board(rows, cols, EMPTY);
 
     // An empty column should not be considered full
-    assert(is_column_full(1, rows, board) == false);
+    assert(is_column_full(1, rows, board) == false && RED "test_is_column_full: FAILED - column should not be full\n" RESET);
 
     // Fill a column completely and test again
     for (int i = 0; i < rows; i++) {
         board[i][0] = PLAYER1;
     }
-    assert(is_column_full(1, rows, board) == true);
+    assert(is_column_full(1, rows, board) == true && RED "test_is_column_full: FAILED - column should be full\n" RESET);
 
     destroy_board(rows, board);
+    printf(GREEN "test_is_column_full: PASSED\n" RESET);
 }
 
 void test_select_smart_column() {
@@ -53,16 +26,17 @@ void test_select_smart_column() {
     board[4][0] = PLAYER2;
     board[3][0] = PLAYER2;
 
-    assert(select_smart_column(cols, rows, board) == 1);
+    assert(select_smart_column(cols, rows, board) == 1 && RED "test_select_smart_column: FAILED - AI should select column 1\n" RESET);
     reset_board(rows, cols, board);
 
     // Simulate a situation where the player can win on the next move
     board[5][1] = PLAYER1;
     board[4][1] = PLAYER1;
     board[3][1] = PLAYER1;
-    assert(select_smart_column(cols, rows, board) == 2);
+    assert(select_smart_column(cols, rows, board) == 2 && RED "test_select_smart_column: FAILED - Player should select column 2\n" RESET);
 
     destroy_board(rows, board);
+    printf(GREEN "test_select_smart_column: PASSED\n" RESET);
 }
 
 void test_select_random_column() {
@@ -72,7 +46,7 @@ void test_select_random_column() {
     // Select a random column on an empty board
     int col = select_random_column(cols, rows, board);
 
-    assert(col == 2);  // The expected value based on the random seed
+    assert(col == 2 && RED "test_select_random_column: FAILED - Expected column 2\n" RESET);
 
     // Fill all columns except one
     for (int j = 0; j < cols - 1; j++) {
@@ -83,17 +57,8 @@ void test_select_random_column() {
 
     // The AI should select the only non-full column, which is column 7 (the last one)
     col = select_random_column(cols, rows, board);
-    assert(col == 7);  // The last remaining column
+    assert(col == 7 && RED "test_select_random_column: FAILED - Expected column 7\n" RESET);
 
     destroy_board(rows, board);
-}
-
-
-int main() {
-    test_is_column_full();
-    test_select_smart_column();
-    test_select_random_column();
-
-    printf("All computer tests passed successfully.\n");
-    return EXIT_SUCCESS;
+    printf(GREEN "test_select_random_column: PASSED\n" RESET);
 }
diff --git a/src/test_computer.h b/src/test_computer.h
new file mode 100644
index 0000000000000000000000000000000000000000..550fc924314224b094078a6000601808c59a5c15
--- /dev/null
+++ b/src/test_computer.h
@@ -0,0 +1,12 @@
+#ifndef _TEST_COMPUTER_H_
+#define _TEST_COMPUTER_H_
+
+#include "computer.h"
+#include "game.h"
+#include "test_puissance4.h"
+
+void test_is_column_full();
+void test_select_smart_column();
+void test_select_random_column();
+
+#endif
diff --git a/src/test_game.c b/src/test_game.c
index 3cd6b996ae61696cbc15c3eac97b2fc91b94ad1a..7f2ec445c382acd8ca3b8e738a015e0a9c767bee 100644
--- a/src/test_game.c
+++ b/src/test_game.c
@@ -1,23 +1,4 @@
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "game.h"
-
-int** setup_board(int rows, int cols, int default_value) {
-    int** board = create_board(rows, cols);
-    if (board == NULL) {
-        fprintf(stderr, "Error: Unable to allocate memory for the board.\n");
-        exit(EXIT_FAILURE);
-    }
-
-    for (int i = 0; i < rows; i++) {
-        for (int j = 0; j < cols; j++) {
-            board[i][j] = default_value;
-        }
-    }
-
-    return board;
-}
+#include "test_game.h"
 
 void test_place_token() {
     int rows = 6, cols = 7;
@@ -26,15 +7,16 @@ void test_place_token() {
     // Player1
     struct coordinate coord = { 2, 3 };
     place_token(0, coord, board);
-    assert(board[2][3] == PLAYER1);
+    assert(board[2][3] == PLAYER1 && RED "test_place_token: FAILED - Expected PLAYER1 at board[2][3]\n" RESET);
 
     // Player2
     coord.x = 4;
     coord.y = 5;
     place_token(1, coord, board);
-    assert(board[4][5] == PLAYER2);
+    assert(board[4][5] == PLAYER2 && RED "test_place_token: FAILED - Expected PLAYER2 at board[4][5]\n" RESET);
 
     destroy_board(rows, board);
+    printf(GREEN "test_place_token: PASSED\n" RESET);
 }
 
 void test_find_empty_slot() {
@@ -44,31 +26,34 @@ void test_find_empty_slot() {
     // Search for an empty slot in a column
     board[5][2] = PLAYER1;
     struct coordinate coord = find_empty_slot(2, rows, board);
-    assert(coord.x == 4 && coord.y == 2);
+    assert(coord.x == 4 && coord.y == 2 && RED "test_find_empty_slot: FAILED - Expected coord (4, 2)\n" RESET);
 
     // Search for an empty slot in a column with multiple tokens
     board[4][2] = PLAYER2;
     coord = find_empty_slot(2, rows, board);
-    assert(coord.x == 3 && coord.y == 2);
+    assert(coord.x == 3 && coord.y == 2 && RED "test_find_empty_slot: FAILED - Expected coord (3, 2)\n" RESET);
 
     destroy_board(rows, board);
+    printf(GREEN "test_find_empty_slot: PASSED\n" RESET);
 }
 
 void test_is_valid_column() {
     int cols = 7;
 
-    assert(is_valid_column(1, cols) == true);
-    assert(is_valid_column(7, cols) == true);
+    assert(is_valid_column(1, cols) == true && RED "test_is_valid_column: FAILED - Column 1 should be valid\n" RESET);
+    assert(is_valid_column(7, cols) == true && RED "test_is_valid_column: FAILED - Column 7 should be valid\n" RESET);
 
-    assert(is_valid_column(0, cols) == false);
-    assert(is_valid_column(8, cols) == false);
+    assert(is_valid_column(0, cols) == false && RED "test_is_valid_column: FAILED - Column 0 should be invalid\n" RESET);
+    assert(is_valid_column(8, cols) == false && RED "test_is_valid_column: FAILED - Column 8 should be invalid\n" RESET);
+    printf(GREEN "test_is_valid_column: PASSED\n" RESET);
 }
 
 void test_is_board_full() {
     int rows = 6, cols = 7;
 
-    assert(is_board_full(41, rows, cols) == true);  // Full board
-    assert(is_board_full(40, rows, cols) == false); // Not full board
+    assert(is_board_full(41, rows, cols) == true && RED "test_is_board_full: FAILED - Board should be full\n" RESET);
+    assert(is_board_full(40, rows, cols) == false && RED "test_is_board_full: FAILED - Board should not be full\n" RESET);
+    printf(GREEN "test_is_board_full: PASSED\n" RESET);
 }
 
 void test_does_player_win() {
@@ -81,7 +66,7 @@ void test_does_player_win() {
     board[5][2] = PLAYER1;
     board[5][3] = PLAYER1;
     struct coordinate coord = { 5, 3 };
-    assert(does_player_win(coord, rows, cols, board) == true);
+    assert(does_player_win(coord, rows, cols, board) == true && RED "test_does_player_win: FAILED - Horizontal win should be detected\n" RESET);
 
     // Test vertical win
     fill_board(rows, cols, board);
@@ -91,7 +76,7 @@ void test_does_player_win() {
     board[2][2] = PLAYER2;
     coord.x = 2;
     coord.y = 2;
-    assert(does_player_win(coord, rows, cols, board) == true);
+    assert(does_player_win(coord, rows, cols, board) == true && RED "test_does_player_win: FAILED - Vertical win should be detected\n" RESET);
 
     // Test diagonal win (left to right)
     fill_board(rows, cols, board);
@@ -101,7 +86,7 @@ void test_does_player_win() {
     board[2][3] = PLAYER1;
     coord.x = 2;
     coord.y = 3;
-    assert(does_player_win(coord, rows, cols, board) == true);
+    assert(does_player_win(coord, rows, cols, board) == true && RED "test_does_player_win: FAILED - Left-to-right diagonal win should be detected\n" RESET);
 
     // Test diagonal win (right to left)
     fill_board(rows, cols, board);
@@ -111,18 +96,8 @@ void test_does_player_win() {
     board[2][0] = PLAYER2;
     coord.x = 2;
     coord.y = 0;
-    assert(does_player_win(coord, rows, cols, board) == true);
+    assert(does_player_win(coord, rows, cols, board) == true && RED "test_does_player_win: FAILED - Right-to-left diagonal win should be detected\n" RESET);
 
     destroy_board(rows, board);
+    printf(GREEN "test_does_player_win: PASSED\n" RESET);
 }
-
-int main() {
-    test_place_token();
-    test_find_empty_slot();
-    test_is_valid_column();
-    test_is_board_full();
-    test_does_player_win();
-
-    printf("All tests passed successfully.\n");
-    return EXIT_SUCCESS;
-}
\ No newline at end of file
diff --git a/src/test_game.h b/src/test_game.h
new file mode 100644
index 0000000000000000000000000000000000000000..47e4b2b36f6c79a3576f37cd953afb14b45b5bfd
--- /dev/null
+++ b/src/test_game.h
@@ -0,0 +1,14 @@
+#ifndef _TEST_GAME_H_
+#define _TEST_GAME_H_
+
+#include "computer.h"
+#include "game.h"
+#include "test_puissance4.h"
+
+void test_place_token();
+void test_find_empty_slot();
+void test_is_valid_column();
+void test_is_board_full();
+void test_does_player_win();
+
+#endif
diff --git a/src/test_puissance4.c b/src/test_puissance4.c
new file mode 100644
index 0000000000000000000000000000000000000000..74004d565109255c37f16d046f25584f29fd5b15
--- /dev/null
+++ b/src/test_puissance4.c
@@ -0,0 +1,47 @@
+#include "test_puissance4.h"
+
+int main() {
+    // game.h
+    test_create_board();
+    test_fill_board();
+    test_destroy_board();
+
+    // computer.h
+    test_is_column_full();
+    test_select_smart_column();
+    test_select_random_column();
+
+    // board.h
+    test_place_token();
+    test_find_empty_slot();
+    test_is_valid_column();
+    test_is_board_full();
+    test_does_player_win();
+
+    printf(GREEN "All tests passed successfully!\n" RESET);
+    return EXIT_SUCCESS;
+}
+
+int** setup_board(int rows, int cols, int default_value) {
+    int** board = create_board(rows, cols);
+    if (board == NULL) {
+        fprintf(stderr, "Error: Unable to allocate memory for the board.\n");
+        exit(EXIT_FAILURE);
+    }
+
+    for (int i = 0; i < rows; i++) {
+        for (int j = 0; j < cols; j++) {
+            board[i][j] = default_value;
+        }
+    }
+
+    return board;
+}
+
+void reset_board(int rows, int cols, int** board) {
+    for (int i = 0; i < rows; i++) {
+        for (int j = 0; j < cols; j++) {
+            board[i][j] = EMPTY;
+        }
+    }
+}
diff --git a/src/test_puissance4.h b/src/test_puissance4.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b1a9300202ad820477ee55bd704b6b217c6c2b2
--- /dev/null
+++ b/src/test_puissance4.h
@@ -0,0 +1,21 @@
+#ifndef _TEST_PUSSIANCE4_h_
+#define _TEST_PUSSIANCE4_h_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "../puissance4.h"
+#include "test_board.h"
+#include "test_game.h"
+#include "test_computer.h"
+
+#define GREEN "\033[1;32m"
+#define RED "\033[1;31m"
+#define RESET "\033[0m"
+
+int** setup_board(int rows, int cols, int default_value);
+void reset_board(int rows, int cols, int** board);
+
+#endif
diff --git a/test_puissance4.c b/test_puissance4.c
deleted file mode 100644
index f63ff0d35fc96b59d6337277ac1545b4fb711c68..0000000000000000000000000000000000000000
--- a/test_puissance4.c
+++ /dev/null
@@ -1,36 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include "puissance4.h"
-
-#define GREEN "\033[1;32m"
-#define RED "\033[1;31m"
-#define RESET "\033[0m"
-
-void test_is_valid_game_mode();
-void test_is_valid_board_size();
-
-int main() {
-    test_is_valid_game_mode();
-    test_is_valid_board_size();
-    return EXIT_SUCCESS;
-}
-
-void test_is_valid_game_mode() {
-    assert(is_valid_game_mode(1) == true);
-    assert(is_valid_game_mode(2) == true);
-    assert(is_valid_game_mode(3) == true);
-    assert(is_valid_game_mode(0) == false);
-    assert(is_valid_game_mode(4) == false);
-    assert(is_valid_game_mode(-1) == false);
-    printf(GREEN "test_is_valid_game_mode: PASSED\n" RESET);
-}
-
-void test_is_valid_board_size() {
-    assert(is_valid_board_size(4, 4) == true);
-    assert(is_valid_board_size(6, 7) == true);
-    assert(is_valid_board_size(3, 4) == false);
-    assert(is_valid_board_size(4, 3) == false);
-    assert(is_valid_board_size(2, 2) == false);
-    printf(GREEN "test_is_valid_board_size: PASSED\n" RESET);
-}
\ No newline at end of file