diff --git a/tests/LaravelGoogleShoppingFeedTest.php b/tests/LaravelGoogleShoppingFeedTest.php
new file mode 100644
index 0000000..af99f1b
--- /dev/null
+++ b/tests/LaravelGoogleShoppingFeedTest.php
@@ -0,0 +1,147 @@
+ 'prod-1',
+ 'link' => 'https://example.com/product',
+ 'title' => 'Test Product',
+ 'g:price' => '29.99 AUD',
+ 'g:image_link' => 'https://example.com/image.jpg',
+ ], $overrides);
+}
+
+describe('init', function () {
+ it('stores title, description, and link', function () {
+ $feed = LaravelGoogleShoppingFeed::init('My Store', 'My Desc', 'https://example.com');
+
+ expect($feed->title)->toBe('My Store')
+ ->and($feed->description)->toBe('My Desc')
+ ->and($feed->link)->toBe('https://example.com');
+ });
+
+ it('defaults to empty strings', function () {
+ $feed = LaravelGoogleShoppingFeed::init();
+
+ expect($feed->title)->toBe('')
+ ->and($feed->description)->toBe('')
+ ->and($feed->link)->toBe('');
+ });
+});
+
+describe('addItem', function () {
+ it('accepts a valid item without throwing', function () {
+ $feed = LaravelGoogleShoppingFeed::init();
+ $feed->addItem(validProduct());
+
+ expect(true)->toBeTrue();
+ });
+
+ it('throws for missing required field', function (string $field) {
+ $product = validProduct();
+ unset($product[$field]);
+
+ expect(fn () => LaravelGoogleShoppingFeed::init()->addItem($product))
+ ->toThrow(MissingRequiredFieldException::class);
+ })->with(['id', 'link', 'title', 'g:price', 'g:image_link']);
+
+ it('throws with a message containing the field name', function () {
+ $product = validProduct();
+ unset($product['g:price']);
+
+ expect(fn () => LaravelGoogleShoppingFeed::init()->addItem($product))
+ ->toThrow(MissingRequiredFieldException::class, "g:price");
+ });
+});
+
+describe('toXml', function () {
+ it('returns a string', function () {
+ $feed = LaravelGoogleShoppingFeed::init('Store', 'Desc', 'https://example.com');
+
+ expect(is_string($feed->toXml()))->toBeTrue();
+ });
+
+ it('contains the RSS xmlns:g attribute', function () {
+ $feed = LaravelGoogleShoppingFeed::init();
+ $xml = $feed->toXml();
+
+ expect($xml)->toContain('xmlns:g="http://base.google.com/ns/1.0"');
+ });
+
+ it('contains the RSS version attribute', function () {
+ $feed = LaravelGoogleShoppingFeed::init();
+ $xml = $feed->toXml();
+
+ expect($xml)->toContain('version="2.0"');
+ });
+
+ it('includes the channel title, description and link', function () {
+ $feed = LaravelGoogleShoppingFeed::init('My Store', 'My Description', 'https://example.com');
+ $xml = $feed->toXml();
+
+ expect($xml)
+ ->toContain('My Store')
+ ->toContain('My Description')
+ ->toContain('https://example.com');
+ });
+
+ it('includes product data', function () {
+ $feed = LaravelGoogleShoppingFeed::init('Store', 'Desc', 'https://example.com');
+ $feed->addItem(validProduct());
+ $xml = $feed->toXml();
+
+ expect($xml)
+ ->toContain('