diff --git a/out/production/infrastructureLogicielleClient/opinion/Book.class b/out/production/infrastructureLogicielleClient/opinion/Book.class
index 8dfdf984bf75a2ed66860015a00782fa24b141a7..f0972e96f383f2128c50e18372491c4e3843f9c3 100644
Binary files a/out/production/infrastructureLogicielleClient/opinion/Book.class and b/out/production/infrastructureLogicielleClient/opinion/Book.class differ
diff --git a/out/production/infrastructureLogicielleClient/opinion/SocialNetwork.class b/out/production/infrastructureLogicielleClient/opinion/SocialNetwork.class
index 63caec3114c6cff4ec8a5d44055e4672e8b70dec..37acf0e72b0dad6549446276e819536d633cdb5d 100644
Binary files a/out/production/infrastructureLogicielleClient/opinion/SocialNetwork.class and b/out/production/infrastructureLogicielleClient/opinion/SocialNetwork.class differ
diff --git a/src/opinion/Book.java b/src/opinion/Book.java
index e2b8f614fd08a75639f3b5c6bca1b79be9d674a4..88f4112b5863ada526e87edf75bcc80c3be923a1 100644
--- a/src/opinion/Book.java
+++ b/src/opinion/Book.java
@@ -1,5 +1,7 @@
 package opinion;
 
+import java.util.LinkedList;
+
 /**
  * This class allows us to create books and to register new books
  */
@@ -8,6 +10,7 @@ public class Book {
     //Declaration of private class variables
     private String title, kind, author, comment;
     private int nbPages;
+    private LinkedList<Review> reviews = new LinkedList<>();
 
     /**
      * Initialization of private variables with constructor
@@ -71,7 +74,25 @@ public class Book {
         String formattedTitle2 = title2.toUpperCase().trim();
         return formattedTitle.equals(formattedTitle2);
     }
+    public void addOrUpdateReview(String login, float mark, String comment) {
+        for (Review r : reviews) {
+            if (r.getReviewerLogin().equalsIgnoreCase(login)) {
+                r.setMark(mark);
+                r.setComment(comment);
+                return;
+            }
+        }
+        reviews.add(new Review(login, mark, comment));
+    }
 
+    public float getAverageMark() {
+        if (reviews.isEmpty()) return 0f;
+        float total = 0f;
+        for (Review r : reviews) {
+            total += r.getMark();
+        }
+        return total / reviews.size();
+    }
     /**
      * String function to display the details of a book
      *
diff --git a/src/opinion/Review.java b/src/opinion/Review.java
new file mode 100644
index 0000000000000000000000000000000000000000..e66dcd2fb1de9b8d8d37c90be426389448c07ac8
--- /dev/null
+++ b/src/opinion/Review.java
@@ -0,0 +1,33 @@
+package opinion;
+
+public class Review {
+    private String reviewerLogin;
+    private float mark;
+    private String comment;
+
+    public Review(String reviewerLogin, float mark, String comment) {
+        this.reviewerLogin = reviewerLogin;
+        this.mark = mark;
+        this.comment = comment;
+    }
+
+    public String getReviewerLogin() {
+        return reviewerLogin;
+    }
+
+    public float getMark() {
+        return mark;
+    }
+
+    public void setMark(float mark) {
+        this.mark = mark;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+}
\ No newline at end of file
diff --git a/src/opinion/SocialNetwork.java b/src/opinion/SocialNetwork.java
index 3a0fd692baf7c37d462bd30faba8f1c34ea5e554..239e01bb0727ec96e78111ec49db3b6a57c97c72 100644
--- a/src/opinion/SocialNetwork.java
+++ b/src/opinion/SocialNetwork.java
@@ -229,12 +229,52 @@ public class SocialNetwork implements ISocialNetwork {
         return ret.trim();
     }
 
+
     @Override
     public float reviewItemBook(String login, String password, String title,
                                 float mark, String comment) throws BadEntryException,
             NotMemberException, NotItemException {
-        // TODO Auto-generated method stub
-        return 0;
+
+        // Validate user
+        validateuser(login, password);
+
+        if (comment == null) {
+            throw new BadEntryException("Comment cannot be null.");
+        }
+
+        if (mark < 0.0f || mark > 5.0f) {
+            throw new BadEntryException("Mark must be between 0.0 and 5.0.");
+        }
+
+        // Verify existence of book
+        boolean bookExists = validatebook(title);
+        if (!bookExists) {
+            throw new NotItemException("Book not found.");
+        }
+
+        // Member authentication
+        if (!authenticateUser(login, password)) {
+            throw new NotMemberException("Invalid login or password.");
+        }
+
+        // Search book
+        Book targetBook = null;
+        for (Book b : listBook) {
+            if (b.compareTitle(title)) {
+                targetBook = b;
+                break;
+            }
+        }
+
+        if (targetBook == null) {
+            throw new NotItemException("Book not found.");
+        }
+
+        // Add or replace a review
+        targetBook.addOrUpdateReview(login.trim(), mark, comment);
+
+        // Returns the new average mark
+        return targetBook.getAverageMark();
     }
 
     @Override
diff --git a/src/tests/ReviewItemBookTest.java b/src/tests/ReviewItemBookTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..109a75e8167ba764f7e57f4b462f83fa9b35fde6
--- /dev/null
+++ b/src/tests/ReviewItemBookTest.java
@@ -0,0 +1,90 @@
+package tests;
+
+import opinion.SocialNetwork;
+import opinion.ISocialNetwork;
+import exceptions.*;
+
+public class ReviewItemBookTest {
+
+    private static int nbTests = 0;
+    private static int nbErrors = 0;
+
+    private static void displaySuccess(String message) {
+        System.out.println("Test passed: " + message);
+    }
+
+    private static void displayError(String message) {
+        System.out.println("Test failed: " + message);
+        nbErrors++;
+    }
+
+    private static void testExpectedAverage(ISocialNetwork sn, String login, String password, String title,
+                                            float mark, String comment, float expectedAverage, String testId) {
+        nbTests++;
+        try {
+            float result = sn.reviewItemBook(login, password, title, mark, comment);
+            if (Math.abs(result - expectedAverage) < 0.01f) {
+                displaySuccess("[" + testId + "] Average = " + result);
+            } else {
+                displayError("[" + testId + "] Expected " + expectedAverage + " but got " + result);
+            }
+        } catch (Exception e) {
+            displayError("[" + testId + "] Unexpected exception: " + e);
+        }
+    }
+
+    private static void testExpectedException(ISocialNetwork sn, String login, String password, String title,
+                                              float mark, String comment, Class<?> expectedException, String testId) {
+        nbTests++;
+        try {
+            sn.reviewItemBook(login, password, title, mark, comment);
+            displayError("[" + testId + "] Expected exception " + expectedException.getSimpleName() + " but none was thrown");
+        } catch (Exception e) {
+            if (e.getClass().equals(expectedException)) {
+                displaySuccess("[" + testId + "] Correctly threw " + expectedException.getSimpleName());
+            } else {
+                displayError("[" + testId + "] Unexpected exception: " + e);
+            }
+        }
+    }
+
+    public static TestReport test() {
+        ISocialNetwork sn = new SocialNetwork();
+
+        try {
+            sn.addMember("ana", "pass123", "casual reader");
+            sn.addMember("luis", "1234pass", "fantasy reader");
+            sn.addItemBook("ana", "pass123", "El Hobbit", "Fantasía", "J.R.R. Tolkien", 300);
+        } catch (Exception e) {
+            System.out.println("Setup failed: " + e.getMessage());
+            return null;
+        }
+
+        // --- Casos exitosos ---
+        testExpectedAverage(sn, "ana", "pass123", "El Hobbit", 4.0f, "Muy bueno", 4.0f, "1.1");
+        testExpectedAverage(sn, "luis", "1234pass", "El Hobbit", 5.0f, "Obra maestra", 4.5f, "1.2");
+        testExpectedAverage(sn, "ana", "pass123", "El Hobbit", 3.0f, "Bueno pero largo", 4.0f, "1.3");
+
+        // --- Casos de error esperados ---
+        testExpectedException(sn, "mario", "nopass", "El Hobbit", 4.0f, "Hola", NotMemberException.class, "3.1");
+        testExpectedException(sn, "ana", "pass123", "Book X", 3.0f, "Does not exist", NotItemException.class, "3.2");
+        testExpectedException(sn, "ana", "pass123", "El Hobbit", 6.0f, "Out of range", BadEntryException.class, "3.3");
+        testExpectedException(sn, "", "pass123", "El Hobbit", 4.0f, "Fantastic book", BadEntryException.class, "3.4");
+        testExpectedException(sn, "ana", "a", "El Hobbit", 4.0f, "Fantastic book", BadEntryException.class, "3.5");
+        testExpectedException(sn, "ana", "pass123", "", 4.0f, "Fantastic book", BadEntryException.class, "3.6");
+        testExpectedException(sn, "ana", "pass123", "El Hobbit", 4.0f, null, BadEntryException.class, "3.7");
+
+        try {
+            TestReport report = new TestReport(nbTests, nbErrors);
+            System.out.println("ReviewItemBookTest : " + report);
+            return report;
+        } catch (NotTestReportException e) {
+            System.out.println("Failed to create TestReport");
+            return null;
+        }
+    }
+
+    public static void main(String[] args) {
+        test();
+    }
+}