Testing - Shravsssss/MovieRecommender GitHub Wiki
Testing for StreamR Movie Recommendation System
Testing plays a critical role in verifying the accuracy and stability of each component within the StreamR movie recommendation system. By isolating individual functions, the tests ensure each piece of the system behaves as expected, helping to catch issues early in development.
Test Suite Overview
The tests in StreamR cover a wide range of functionalities, focusing on the following areas:
- Recommendation Logic: Validates that recommendations are correctly generated based on various user preferences.
- Data Processing: Ensures data normalization, standardization, and cleaning functions operate correctly, handling diverse cases, including missing data and special characters.
- Error Handling: Tests that functions handle errors gracefully, providing informative messages and avoiding unexpected crashes.
- User Interaction Functions: Verifies that functions handling user inputs and account preferences work reliably and return expected results.
Each test targets specific functionality in isolation, ensuring that each part of the system performs its intended task accurately and consistently.
Test Implementation
The tests are implemented using Python's unittest
framework, providing a structured environment to define and execute individual test cases. Each test function verifies that a specific method or component behaves correctly under both normal and edge-case conditions.
Example Test Cases
The following examples illustrate tests for key functionalities in the recommendation system.
1. Testing the Authentication Process
Although this is just a part of all the test cases we have for authentication, the main purpose of this part is to make sure that the user is correctly added to the database while signing up for the system and logging back in later, and it covers various scenarios considering the username and the password.
class AuthTests(unittest.TestCase):
"""Test suite for authentication and user account management in RecommenderApp."""
def setUp(self):
"""Set up test client and initialize database."""
self.app = app.test_client()
self.app.testing = True
with app.app_context():
db.create_all()
def tearDown(self):
"""Tear down database after each test."""
with app.app_context():
db.session.remove()
db.drop_all()
def register(self, username, email, password):
"""Helper function to register a user."""
return self.app.post('/register', data=dict(
username=username,
email=email,
password=password
), follow_redirects=True)
def login(self, username, password):
"""Helper function to log in a user."""
return self.app.post('/login', data=dict(
username=username,
password=password
), follow_redirects=True)
def logout(self):
"""Helper function to log out a user."""
return self.app.get('/logout', follow_redirects=True)
def test_register_new_user(self):
"""Test registering a new user."""
response = self.register(
"newuser",
"[email protected]",
"password123")
# Expecting successful redirect to landing page or registration page
self.assertEqual(response.status_code, 200)
def test_register_existing_user(self):
"""Test registering a user with an existing username or email."""
self.register(
"existinguser",
"[email protected]",
"password123")
response = self.register(
"existinguser",
"[email protected]",
"password123")
# Should be redirected due to already existing user
self.assertEqual(response.status_code, 200)
2. Testing Data Standardization
Test cases from the previous project that cover the input validity of the user while searching for movies are shown below.
def testMultipleMovies(self):
ts = [
{"title": "Harry Potter and the Goblet of Fire (2005)", "rating": 5.0},
{"title": "Twilight Saga: New Moon, The (2009)", "rating": 5.0},
]
recommendations = recommendForNewUser(ts)
self.assertTrue(("Twilight (2008)" in recommendations))
def testEmptyInput(self):
ts = []
recommendations = recommendForNewUser(ts)
self.assertEqual(
recommendations,
[],
"Empty input should result in empty recommendations")
def testInvalidRating(self):
ts = [{"title": "Toy Story (1995)", "rating": 6.0}]
recommendations = recommendForNewUser(ts)
self.assertEqual(
recommendations,
[],
"Invalid rating should result in empty recommendations")
def testTooLongMovieTitle(self):
ts = [{"title": "T" * 1000, "rating": 5.0}]
recommendations = recommendForNewUser(ts)
self.assertEqual(
recommendations,
[],
"Too long movie titles should result in empty recommendations")
def testInvalidTitle(self):
ts = [
{"title": "Invalid Movie", "rating": 5.0}, # Incorrect key "title"
]
recommendations = recommendForNewUser(ts)
self.assertEqual(
recommendations,
[],
"Invalid input should result in empty recommendations")
3. Testing the Functionality
The main algorithm and procedure of all different functions are tested in this part, which has various scenarios for filters, retrieving movies, going through the profiles, watchlists, and the search function itself. As can be seen in the repository, there are multiple files, each focusing on one specific function for higher-quality testing. For the filtering part, the tests focus on trying different search styles and genres, covering all different styles like one input or more than one genre. Moreover, the filtering part tries to validate the reviews and the movies that were returned from TMDB.
import unittest
from unittest.mock import patch
from Code.recommenderapp.tmdb_utils import search_movie_tmdb, get_streaming_providers, get_movie_reviews
class TestTMDBFunctions(unittest.TestCase):
"""
Test suite for TMDB API-related functions in the RecommenderApp.
Includes tests for searching movies by name and year, retrieving
streaming providers, and fetching reviews for a movie.
"""
# Mock API key for testing purposes
TMDB_API_KEY = "9f385440fe752884a4f5b8ea5b6839dd"
# Test cases for `search_movie_tmdb`
@patch('requests.get')
def test_search_movie_tmdb_valid(self, mock_get):
"""
Test search_movie_tmdb with a valid movie name, expecting
a valid movie ID to be returned.
"""
# Mock a successful response with a valid movie ID
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {"results": [{"id": 12345}]}
movie_id = search_movie_tmdb("Inception", self.TMDB_API_KEY)
self.assertEqual(
movie_id,
12345,
"Expected valid movie ID for 'Inception'")
@patch('requests.get')
def test_search_movie_tmdb_invalid_name(self, mock_get):
"""
Test search_movie_tmdb with a nonexistent movie name, expecting
None to be returned.
"""
# Mock a successful response with no results
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {"results": []}
movie_id = search_movie_tmdb("xxxxxxxxxxxxxx", self.TMDB_API_KEY)
self.assertIsNone(movie_id, "Expected None for nonexistent movie name")
The history tests corroborate the searched movies by the users with different profiles and make sure all the setups for that are working properly.
import unittest
from unittest.mock import patch
from Code.recommenderapp.tmdb_utils import search_movie_tmdb, get_streaming_providers, get_movie_reviews
def test_multiple_recommendations_history(self):
"""Test viewing history with multiple recommendations."""
with app.app_context():
rec1 = Recommendation(
user_id=self.user_id,
movie_title="Inception",
recommended_on=datetime.utcnow()
)
rec2 = Recommendation(
user_id=self.user_id,
movie_title="The Matrix",
recommended_on=datetime.utcnow()
)
db.session.add_all([rec1, rec2])
db.session.commit()
self.login_user()
response = self.app.get('/history', follow_redirects=True)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Inception', response.data)
self.assertIn(b'The Matrix', response.data)
The movie tests go deep into the data retrieval process and what properties are returned, such as the rates, reviews, etc. Moreover, it validates the naming style of the movie and how the returned values are sanitized and eventually shown.
import unittest
from Code.prediction_scripts.item_based import recommendForNewUser
import os
import pandas as pd
project_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class RecommendationTests(unittest.TestCase):
"""Test suite for the recommendForNewUser function in RecommenderApp.
This suite includes tests for:
- Valid and empty movie lists.
- Handling duplicates, missing data, and edge cases.
- Filters for rating constraints.
- Case insensitivity and unknown movie titles.
"""
def test_recommendation_valid_movies(self):
"""Test recommendation with a valid list of movies.
Verifies that recommendations are generated when a valid movie title
with a valid rating is provided.
"""
movies = [{"title": "Inception (2010)", "rating": 5.0}]
self.assertTrue(
recommendForNewUser(movies),
"Expected recommendations")
def test_recommendation_empty_movies(self):
"""Test recommendation with an empty movie list.
Verifies that an empty list is returned when no movies are provided.
"""
movies = []
self.assertEqual(recommendForNewUser(movies), [],
"Expected no recommendations")
def test_recommendation_duplicate_movies(self):
"""Test recommendation with duplicate movie entries.
Verifies that recommendations do not duplicate the user's rated movies.
"""
movies = [{"title": "Inception (2010)", "rating": 5.0}, {
"title": "Inception (2010)", "rating": 5.0}]
self.assertTrue(recommendForNewUser(movies),
"Expected recommendations without duplicates")
The search tests try to find the movies using the input.
from Code.recommenderapp.search import Search
import unittest
import warnings
import sys
import os
sys.path.append("../")
warnings.filterwarnings("ignore")
class Tests(unittest.TestCase):
def testSearchToy(self):
search_word = "toy"
search = Search()
filtered_dict = search.resultsTop10(search_word)
expected_resp = [
"Toy Story (1995)",
"Toys (1992)",
"Toy Story 2 (1999)",
"Toy, The (1982)",
"Toy Soldiers (1991)",
"Toy Story 3 (2010)",
"Babes in Toyland (1961)",
"Babes in Toyland (1934)",
]
self.assertTrue(filtered_dict == expected_resp)
def testSearchLove(self):
search_word = "love"
search = Search()
filtered_dict = search.resultsTop10(search_word)
expected_resp = [
"Love & Human Remains (1993)",
"Love Affair (1994)",
"Love and a .45 (1994)",
"Love in the Afternoon (1957)",
"Love Bug, The (1969)",
"Love Jones (1997)",
"Love and Other Catastrophes (1996)",
"Love Serenade (1996)",
"Love and Death on Long Island (1997)",
"Love Is the Devil (1998)",
]
self.assertTrue(filtered_dict == expected_resp)
Running Tests
To execute the unit test suite, navigate to the project's root directory and run the following command:
python -m unittest discover -s test -p "*.py"
This command discovers and executes all test cases within the test
directory, allowing for automated and efficient testing of each system component.
Contributions to Testing
Expanding the test suite is encouraged. When adding new features or refactoring existing ones, ensure that tests are updated or added to verify functionality. Detailed test cases enhance reliability and ease future maintenance.
For more information on contributing to the test suite, refer to the project's testing code.