Testing Strategy
Relevant source files
The following files were used as context for generating this wiki page:
- phpunit.xml
- tests/Feature/Api/ProductApiTest.php
- tests/Unit/Services/AutoCreateStoreTest.php
- tests/Feature/Filament/ProductTest.php
- tests/Feature/Models/UrlTest.php
- README.md
- tests/Feature/Api/StoreApiTest.php
- tests/Feature/Api/TagApiTest.php
- tests/Feature/Filament/StoreTest.php
- tests/Feature/Models/ProductTest.php
- tests/Unit/Services/ScrapeUrlTest.php
- tests/Feature/Integrations/SearchXNG/SearchResearchTest.php
- config/price_buddy.php
The PriceBuddy project employs a comprehensive testing strategy using PHPUnit, covering multiple layers of the application from individual units of code to full-feature browser interactions. The strategy emphasizes a clean separation between Unit and Feature tests, ensuring both isolated component verification and integrated system functionality. The testing environment is configured to be isolated and repeatable, using an in-memory cache, a dedicated test database, and synchronous queues to provide fast and consistent feedback.
The tests are organized to mirror the application's structure, with dedicated tests for API endpoints, data models, Filament UI components, and internal services. This multi-faceted approach ensures high code quality, maintainability, and confidence in the application's stability.
Sources: phpunit.xml, README.md:39-41
Test Environment and Configuration
The testing environment is defined in phpunit.xml. It sets up specific environment variables to ensure tests run in an isolated and controlled context, separate from the development or production environments.
Sources: phpunit.xml:1-33
Environment Variables
The following environment variables are configured for the test suite:
| Variable | Value | Purpose |
|---|---|---|
APP_ENV | testing | Identifies the environment as 'testing' for Laravel. |
BCRYPT_ROUNDS | 4 | Reduces bcrypt rounds to speed up hashing in tests. |
CACHE_STORE | array | Uses a non-persistent, in-memory array driver for caching. |
DB_HOST | tests_db | Specifies the hostname for the dedicated test database. |
MAIL_MAILER | array | Prevents emails from being sent, storing them in an array instead. |
QUEUE_CONNECTION | sync | Executes queued jobs synchronously, avoiding the need for a queue worker. |
SESSION_DRIVER | array | Uses a non-persistent array driver for session management. |
PULSE_ENABLED | false | Disables Laravel Pulse during tests. |
TELESCOPE_ENABLED | false | Disables Laravel Telescope during tests. |
Sources: phpunit.xml:18-28
Test Suites
The project defines two primary test suites in phpunit.xml, which logically separate the types of tests being run.
Sources: phpunit.xml:8-14
- Unit Suite: Contains tests for individual classes and methods in isolation. These tests are located in the
tests/Unitdirectory. They focus on small, specific pieces of functionality. - Feature Suite: Contains tests that verify the functionality of larger application features, often involving multiple interacting components like controllers, models, and views. These are located in the
tests/Featuredirectory.
Feature Testing
Feature tests form the bulk of the test suite and cover the application's core functionalities from different perspectives: API, UI (Filament), Models, and Integrations. Most feature tests use the RefreshDatabase trait to ensure a clean database state for each test.
Sources: tests/Feature/Api/ProductApiTest.php:12
API Testing
The API tests validate the behavior of the /api endpoints. They ensure correct responses, data handling, authentication, and authorization.
Key Characteristics:
- Authentication: Tests require a valid user token, which is set up in the
setUp()method and passed in headers for subsequent requests. - CRUD Operations: Full test coverage for Create, Read, Update, and Delete operations using
getJson,postJson,putJson, anddeleteJson. - Authorization: Tests verify that users can only access and manipulate their own resources. Attempts to access another user's data result in a
404 Not Foundresponse. - Validation: Invalid data submissions are tested to ensure they return a
422 Unprocessableresponse with appropriate validation error messages. - Querying Features: Functionality like filtering, sorting, pagination, and including related resources (
?include=...) is thoroughly tested.
The following diagram illustrates the sequence of a successful API request to create a product.
This diagram shows the flow for test_can_create_product where the scraper is mocked to return predefined data.
Sources: tests/Feature/Api/ProductApiTest.php:111-140
Filament UI Testing
The admin panel, built with Filament, is tested using Livewire's testing utilities. These tests simulate user interactions within the admin interface, such as creating and editing records.
Key Characteristics:
- Page Rendering: Tests assert that Filament resource pages (
index,create,edit) load successfully with an HTTP 200 status. - Component Interaction:
Livewire::test()is used to interact with Filament's Livewire components. - Form Handling: Tests fill out forms using
fillForm()and submit them by calling actions likesave()orcreate(). - Assertions: Assertions like
assertHasNoFormErrors()are used to verify successful form submissions, and database assertions confirm that the data was persisted correctly.
Sources: tests/Feature/Filament/ProductTest.php, tests/Feature/Filament/StoreTest.php, tests/Feature/Filament/UserTest.php
Model Testing
Model tests verify the logic encapsulated within the Eloquent models. This includes relationships, scopes, accessors, mutators, and custom business logic.
The diagram below shows the relationships between the core models as tested in the suite.
This class diagram illustrates the key model relationships tested, such as a Product having multiple Urls, and each Url having multiple Prices.
Sources: tests/Feature/Models/ProductTest.php, tests/Feature/Models/UrlTest.php, tests/Feature/Models/StoreTest.php
Integration Testing
Integration tests verify the interaction between the application and external services. A key example is the integration with SearXNG for product searching.
In SearchResearchTest.php, the test:
- Fakes HTTP requests to the SearXNG instance and product pages.
- Dispatches a
CacheSearchResultsjob to simulate a user search. - Asserts that the search results are correctly parsed, stored in the
url_researchestable, and that prices are scraped. - Tests the Livewire components (
CreateViaSearchForm,CreateViaSearchTable) that use this data.
This ensures the entire search-and-add feature works as expected, with external dependencies mocked for reliability.
Sources: tests/Feature/Integrations/SearchXNG/SearchResearchTest.php
Unit Testing
Unit tests focus on individual classes or services in isolation. They are crucial for testing complex business logic without the overhead of the full application framework.
Service Testing
Services like ScrapeUrl and AutoCreateStore are tested by mocking their external dependencies.
ScrapeUrlTest: This test verifies theScrapeUrlservice. It usesHttp::fake()to provide a mock HTML response and asserts that the service correctly extracts data or logs an error if required fields are missing. It also tests retry logic on failure.AutoCreateStoreTest: This test validates the logic for automatically creating a store configuration from a URL. It uses a variety of HTML fixture files to simulate different website structures and asserts that theAutoCreateStoreservice can correctly identify the right scraping strategy (e.g., using meta tags, CSS selectors, or regex).
The flow for the AutoCreateStore service is shown below.
This diagram shows how the AutoCreateStore service attempts multiple strategies to find product data, as configured in config/price_buddy.php and verified in AutoCreateStoreTest.php.
Sources: tests/Unit/Services/AutoCreateStoreTest.php, config/price_buddy.php:23-64
Test Helpers
The ScraperTrait is a common helper used across different tests (e.g., ProductApiTest, UrlTest) to mock the scraping functionality. This avoids making actual HTTP requests during tests and provides a consistent way to control the data returned by the scraper.
Sources: tests/Feature/Api/ProductApiTest.php:11, tests/Unit/Services/ScrapeUrlTest.php:13
// Example from ScraperTrait used in tests
$this->mockScrape('$99.99', 'Test Product', 'https://example.com/image.jpg');