Skip to content

Development: Increase Test Coverage of the Exam Class #10921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from

Conversation

SamuelRoettgermann
Copy link

@SamuelRoettgermann SamuelRoettgermann commented May 26, 2025

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the server coding and design guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).

Motivation and Context

Test coverage should be increased.

Description

Increases test coverage of the Exam class to effectively 100%.
I also made a few, small changes to the Exam class itself, where I changed method return types from boxed to primitive versions and simplified a few lines to assume non-null where it was either impossible to have a null value in the first place, or where, if called with null, would lead to a NullPointerException anyway, or where the null return value was never used and also doesn't really make sense.
Exam.java#title is already non-nullable, even in the database (run SHOW columns FROM exam to verify yourself).
I made sure to use appropriate APIs rather than calling the save function where appropriate.

Steps for Testing

Run the ExamIntegrationTest and verify that the tests in there work.

Exam Mode Testing

Prerequisites:

  • 1 Instructor
  • 1 Student
  1. Log in to Artemis as Instructor
  2. Create a new exam where you try these:
    2.1. Try to omit the title - you should not be able to create the exam, i.e., hit the "Save" button
    2.2. Try to omit the "Visible From" field - you should again not be able to create the exam
    2.3. Try to omit the "Start of Working Time" - you should again not be able to create the exam
  3. Set the "Visible From" to a time in the future and check if you can see the exam as the student, which you shouldn't be able to do.
  4. Set the "Visible From" to a time in the past and check if you can see the exam as the student, which you should be able to do.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Exam Mode Test

  • Test 1
  • Test 2

Test Coverage

Class/File Line Coverage Confirmation (assert/expect)
Exam.java 100%

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of exam visibility, start, and result publication statuses to always return clear true/false values, avoiding ambiguous or missing information.
    • Exam titles now consistently remove leading and trailing spaces.
  • Tests

    • Expanded test coverage to verify exam title trimming, examiner field handling, correction rounds, course name, quiz exam points, exam visibility logic, and exam archive presence.
    • Added tests for edge cases and error scenarios to ensure robust exam property management.

@github-project-automation github-project-automation bot moved this to Work In Progress in Artemis Development May 26, 2025
@github-actions github-actions bot added tests server Pull requests that update Java code. (Added Automatically!) exam Pull requests that affect the corresponding module labels May 26, 2025
Copy link

github-actions bot commented Jun 2, 2025

End-to-End (E2E) Test Results Summary

TestsPassed ✅Skipped ⚠️FailedTime ⏱
End-to-End (E2E) Test Report201 ran198 passed3 skipped0 failed56m 21s 104ms
TestResultTime ⏱
No test annotations available

@SamuelRoettgermann SamuelRoettgermann changed the title increase exam.domain.Exam test coverage. Development: Increase Test Coverage of the Exam Class Jun 3, 2025
@SamuelRoettgermann SamuelRoettgermann marked this pull request as ready for review June 3, 2025 20:23
Copy link
Contributor

coderabbitai bot commented Jun 3, 2025

Walkthrough

The changes update the Exam domain model to enforce non-null and trimmed titles, and switch several status methods from nullable Boolean to primitive boolean, ensuring they always return true or false. Related repository filtering is streamlined, and the integration test suite is expanded with comprehensive tests covering exam properties, state logic, and serialization.

Changes

File(s) Change Summary
src/main/java/de/tum/cit/aet/artemis/exam/domain/Exam.java Setter for title now requires non-null input and trims whitespace. Status methods (isVisibleToStudents, isStarted, resultsPublished) now return primitive boolean and never null; Javadoc updated accordingly.
src/main/java/de/tum/cit/aet/artemis/exam/repository/ExamRepository.java Filtering logic for visible exams simplified using a method reference (Exam::isVisibleToStudents) instead of a lambda.
src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java Multiple new and enhanced test methods added, covering exam title trimming, examiner, correction rounds, course name, quiz exam max points, visibility logic, date misconfiguration, exam archive path, and student exam end logic.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Exam
    participant ExamRepository

    Client->>Exam: setTitle(title)
    Exam-->>Exam: Trim whitespace, require non-null

    Client->>Exam: isVisibleToStudents()
    Exam-->>Client: Return true/false (never null)

    Client->>ExamRepository: filterVisibleExams()
    ExamRepository->>Exam: isVisibleToStudents()
    Exam-->>ExamRepository: Return true/false
    ExamRepository-->>Client: List of visible exams
Loading

Suggested labels

ready for review

Suggested reviewers

  • krusche
  • HawKhiem

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.38.1)
src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java (1)

1325-1348: Test logic is correct, but could be more concise.

The test properly validates courseName handling for both null and non-null cases. Consider using a parameterized test to reduce code duplication:

-    @Test
-    @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR")
-    void testGetCourseName() throws Exception {
-        // First we test for an actual name
-        {
-            final String testCourseName = "Course Name for Testing";
-            final Exam examA = ExamFactory.generateExam(course1);
-            examA.setCourseName(testCourseName);
-            assertThat(examA.getCourseName()).isEqualTo(testCourseName);
-            final URI createdExamURI = request.post("/api/exam/courses/" + course1.getId() + "/exams", examA, HttpStatus.CREATED);
-            Exam receivedExam = request.get(String.valueOf(createdExamURI), HttpStatus.OK, Exam.class);
-            assertThat(receivedExam.getCourseName()).isEqualTo(testCourseName);
-        }
-
-        // Here we test for null
-        {
-            final Exam examB = ExamFactory.generateExam(course1);
-            examB.setCourseName(null);
-            assertThat(examB.getCourseName()).isNull();
-            final URI createdExamURI = request.post("/api/exam/courses/" + course1.getId() + "/exams", examB, HttpStatus.CREATED);
-            Exam receivedExam = request.get(String.valueOf(createdExamURI), HttpStatus.OK, Exam.class);
-            assertThat(receivedExam.getCourseName()).isNull();
-        }
-    }
+    @ParameterizedTest
+    @MethodSource("provideCourseNames")
+    @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR")
+    void testGetCourseName(String courseName) throws Exception {
+        final Exam exam = ExamFactory.generateExam(course1);
+        exam.setCourseName(courseName);
+        assertThat(exam.getCourseName()).isEqualTo(courseName);
+        final URI createdExamURI = request.post("/api/exam/courses/" + course1.getId() + "/exams", exam, HttpStatus.CREATED);
+        Exam receivedExam = request.get(String.valueOf(createdExamURI), HttpStatus.OK, Exam.class);
+        assertThat(receivedExam.getCourseName()).isEqualTo(courseName);
+    }
+
+    private static Stream<Arguments> provideCourseNames() {
+        return Stream.of(
+            Arguments.of("Course Name for Testing"),
+            Arguments.of((String) null)
+        );
+    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d76e06 and 48beb34.

📒 Files selected for processing (3)
  • src/main/java/de/tum/cit/aet/artemis/exam/domain/Exam.java (3 hunks)
  • src/main/java/de/tum/cit/aet/artemis/exam/repository/ExamRepository.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`src/main/java/**/*.java`: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,de...

src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

  • src/main/java/de/tum/cit/aet/artemis/exam/repository/ExamRepository.java
  • src/main/java/de/tum/cit/aet/artemis/exam/domain/Exam.java
`src/test/java/**/*.java`: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_...

src/test/java/**/*.java: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true

  • src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (12)
src/main/java/de/tum/cit/aet/artemis/exam/domain/Exam.java (4)

447-455: Excellent improvement to method contract and predictability.

The change from Boolean to primitive boolean for isVisibleToStudents() is a significant improvement that:

  • Eliminates null return values, making the API more predictable
  • Follows the coding guidelines preference for primitives
  • Reduces the need for null checks in calling code
  • Provides sensible default behavior (returns false when visibleDate is null)

460-468: Consistent improvement aligns with visibility method changes.

The isStarted() method changes mirror the isVisibleToStudents() improvements perfectly. The primitive boolean return type with false fallback for null dates provides consistent, predictable behavior across the exam state methods.


476-481: Completes the trilogy of boolean state method improvements.

The resultsPublished() method change maintains consistency with the other two boolean state methods. All three methods now follow the same pattern: primitive boolean return type with false for null date conditions.


178-481:

✅ Verification successful

Verify breaking change impact across the codebase.

The return type changes from Boolean to boolean for the three state methods represent a breaking change. Any code that previously checked for null return values will need updates.


🏁 Script executed:

#!/bin/bash
# Description: Check for potential breaking changes where null checks were used on the modified boolean methods
# Expected: Find any code that checks for null on these methods

echo "Searching for null checks on the modified boolean methods..."
rg -A 3 -B 3 "isVisibleToStudents\(\)\s*==\s*null|null\s*==\s*isVisibleToStudents\(\)"
rg -A 3 -B 3 "isStarted\(\)\s*==\s*null|null\s*==\s*isStarted\(\)"  
rg -A 3 -B 3 "resultsPublished\(\)\s*==\s*null|null\s*==\s*resultsPublished\(\)"

echo "Searching for Boolean.TRUE.equals() patterns on these methods..."
rg -A 3 -B 3 "Boolean\.TRUE\.equals\(.*isVisibleToStudents\(\)"
rg -A 3 -B 3 "Boolean\.TRUE\.equals\(.*isStarted\(\)"
rg -A 3 -B 3 "Boolean\.TRUE\.equals\(.*resultsPublished\(\)"

Length of output: 4048


No code-level null checks found on Exam methods

I searched for any comparisons against null or uses of Boolean.TRUE.equals(...) on the three modified methods in Exam.java — isVisibleToStudents(), isStarted(), and resultsPublished(). All matches point to calls on StudentExam (e.g. in StudentExamResource.java and StudentExam.java), not on the Exam entity itself.

– StudentExamResource and StudentExam use their own wrapper-based logic and are unaffected by the change in Exam.
– No instances of exam.isStarted() == null, exam.isVisibleToStudents() == null, or Boolean.TRUE.equals(exam.resultsPublished()) exist in the codebase.

You can safely convert those three getters to boolean. Just be aware that JSON serialization will now output false instead of null when the underlying value is unset—verify any API or frontend clients don’t depend on a null value here.

src/main/java/de/tum/cit/aet/artemis/exam/repository/ExamRepository.java (1)

489-489: Perfect simplification leveraging the domain model improvements.

The change from exam -> Boolean.TRUE.equals(exam.isVisibleToStudents()) to Exam::isVisibleToStudents is an excellent improvement that:

  • Leverages the new primitive boolean return type from Exam.isVisibleToStudents()
  • Eliminates the need for Boolean.TRUE.equals() null-safe comparison
  • Follows the coding guidelines preference for static member references
  • Improves readability and reduces complexity

This change perfectly complements the domain model updates in Exam.java.

src/test/java/de/tum/cit/aet/artemis/exam/ExamIntegrationTest.java (7)

42-44: LGTM!

The Jackson imports are correctly added and necessary for the serialization/deserialization tests in the testQuizExamMaxPoints method.


1263-1273: Good test coverage for title trimming!

This addition properly tests the trimming behavior mentioned in the PR objectives. The test correctly validates that exam titles with leading and trailing whitespace are trimmed when retrieved.


1281-1324: Comprehensive test coverage for examiner and correction rounds!

These tests effectively validate:

  • Examiner name persistence
  • Default correction rounds behavior when null
  • Valid correction round scenarios for both test and real exams

The helper method testCreateExamWithCorrectionRoundsAndExpectedCreationStatus is a good abstraction that reduces code duplication.


1350-1381: Excellent test coverage for the transient quizExamMaxPoints field!

The test comprehensively validates:

  • Direct getter/setter functionality
  • JSON serialization includes the field
  • JSON deserialization properly sets the field
  • Various edge cases from 0 to 1000

The parameterized approach with diverse test values ensures robust coverage.


1383-1438: Well-structured tests for the updated boolean visibility methods!

These tests effectively validate the domain changes mentioned in the PR summary where methods like isVisibleToStudents() and isStarted() now return primitive boolean instead of nullable Boolean. The tests properly cover:

  • Various time offsets for visibility checks
  • Null date handling that prevents exam creation
  • Both positive and negative test cases

The parameterized approach with @ValueSource is clean and efficient.


1441-1514: Excellent comprehensive test for exam end time calculation!

This test thoroughly covers all scenarios for isAfterLatestStudentExamEnd():

  • Default behavior with no student exams
  • Regular exams where all students have the same working time
  • Exams with minor time advantages that don't affect the overall end
  • Exams where student time advantages extend beyond the regular end time

The clear separation of test cases with descriptive comments makes the complex business logic easy to understand.


1516-1533: Good edge case coverage for exam archive path validation!

The test properly validates the hasExamArchive() boolean method with:

  • null path → false
  • Empty string → false
  • Valid paths → true

The helper method effectively reduces code duplication while maintaining clarity.

@github-project-automation github-project-automation bot moved this from Work In Progress to Ready For Review in Artemis Development Jun 3, 2025
@SamuelRoettgermann SamuelRoettgermann changed the title Development: Increase Test Coverage of the Exam Class Development: Increase Test Coverage of the Exam Class Jun 3, 2025
Copy link

github-actions bot commented Jun 3, 2025

End-to-End (E2E) Test Results Summary

TestsPassed ✅Skipped ⚠️FailedTime ⏱
End-to-End (E2E) Test Report201 ran198 passed3 skipped0 failed55m 9s 498ms
TestResultTime ⏱
No test annotations available

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
exam Pull requests that affect the corresponding module ready for review server Pull requests that update Java code. (Added Automatically!) tests
Projects
Status: Ready For Review
Development

Successfully merging this pull request may close these issues.

1 participant