testing-patterns.mdc•15.1 kB
---
description: SFCC testing patterns and test automation
globs: "**/test/**/*.js"
alwaysApply: false
---
# SFCC Testing Patterns
Use this rule when creating tests for SFCC components.
## Mandatory MCP Tools Sequence for Testing
**BEFORE writing ANY SFCC tests:**
1. `mcp_sfcc-dev_search_best_practices` with query: "testing"
2. `mcp_sfcc-dev_get_best_practice_guide` with guideName: relevant guide for component under test
3. `mcp_sfcc-dev_search_sfcc_classes` with query: classes being tested
4. `mcp_sfcc-dev_get_sfcc_class_info` with className: specific classes under test
## MCP-Guided Testing Development Process
### Step 1: Research Testing Best Practices
```
Use: mcp_sfcc-dev_search_best_practices with query: "testing"
Purpose: Get SFCC-specific testing patterns and guidelines
```
### Step 2: Understand Component Being Tested
```
Use: mcp_sfcc-dev_get_best_practice_guide with guideName: [relevant guide]
Purpose: Get implementation patterns for the component type being tested
```
### Step 3: Research SFCC Classes Under Test
```
Use: mcp_sfcc-dev_search_sfcc_classes with query: [component domain]
Use: mcp_sfcc-dev_get_sfcc_class_info with className: [specific class]
Purpose: Understand the SFCC APIs and methods being tested
```
## MCP-Enhanced Test Template Pattern
```javascript
'use strict';
/**
 * Test Suite: [Component Name]
 * Purpose: Test [component functionality]
 *
 * Implementation based on:
 * - mcp_sfcc-dev_search_best_practices with query: "testing"
 * - mcp_sfcc-dev_get_best_practice_guide with guideName: "[relevant guide]"
 * - mcp_sfcc-dev_search_sfcc_classes with query: "[domain]"
 */
var assert = require('chai').assert;
var proxyquire = require('proxyquire').noCallThru().noPreserveCache();
describe('ComponentName', function () {
    var ComponentName;
    var mockDependencies;
    var mockLogger;
    beforeEach(function () {
        // Mock SFCC dependencies (patterns from MCP testing best practices)
        mockLogger = {
            error: sinon.spy(),
            warn: sinon.spy(),
            info: sinon.spy(),
            debug: sinon.spy()
        };
        mockDependencies = {
            'dw/system/Logger': {
                getLogger: function () {
                    return mockLogger;
                }
            },
            'dw/system/Transaction': {
                wrap: function (callback) {
                    return callback();
                }
            },
            // Add other SFCC mocks based on MCP class research
        };
        ComponentName = proxyquire('../../../cartridge/scripts/ComponentName', mockDependencies);
    });
    describe('Happy Path Tests', function () {
        it('should handle valid input correctly', function () {
            // Arrange (use patterns from MCP best practices)
            var validInput = 'valid-test-input';
            // Act
            var result = ComponentName.methodName(validInput);
            // Assert (comprehensive validation from MCP testing patterns)
            assert.isTrue(result.success, 'Operation should succeed');
            assert.isDefined(result.data, 'Result data should be defined');
            assert.isString(result.data.someProperty, 'Property should be string');
        });
        it('should process multiple valid inputs', function () {
            var inputs = ['input1', 'input2', 'input3'];
            var results = inputs.map(function(input) {
                return ComponentName.methodName(input);
            });
            results.forEach(function(result, index) {
                assert.isTrue(result.success, 'Input ' + index + ' should succeed');
                assert.isDefined(result.data, 'Result ' + index + ' should have data');
            });
        });
    });
    describe('Error Handling Tests', function () {
        it('should handle null input gracefully', function () {
            // Test null input (critical pattern from MCP testing practices)
            var result = ComponentName.methodName(null);
            assert.isFalse(result.success, 'Null input should fail gracefully');
            assert.isDefined(result.error, 'Error message should be provided');
            assert.equal(result.error, 'INVALID_INPUT', 'Should return expected error code');
        });
        it('should handle undefined input gracefully', function () {
            var result = ComponentName.methodName(undefined);
            assert.isFalse(result.success, 'Undefined input should fail gracefully');
            assert.isDefined(result.error, 'Error message should be provided');
        });
        it('should handle invalid input types', function () {
            var invalidInputs = [123, {}, [], true];
            invalidInputs.forEach(function(invalidInput) {
                var result = ComponentName.methodName(invalidInput);
                assert.isFalse(result.success, 'Invalid input type should fail');
                assert.isDefined(result.error, 'Error should be defined for invalid input');
            });
        });
        it('should handle errors from dependencies', function () {
            // Mock dependency error (from MCP error handling patterns)
            var errorDependencies = Object.assign({}, mockDependencies, {
                'dw/system/SomeManager': {
                    getObject: function() {
                        throw new Error('Dependency error');
                    }
                }
            });
            var ComponentWithError = proxyquire('../../../cartridge/scripts/ComponentName', errorDependencies);
            var result = ComponentWithError.methodName('validInput');
            assert.isFalse(result.success, 'Should handle dependency errors');
            assert.isDefined(result.error, 'Error message should be provided');
            assert.isTrue(mockLogger.error.called, 'Should log errors');
        });
    });
    describe('Edge Cases and Boundary Tests', function () {
        it('should handle empty string input', function () {
            var result = ComponentName.methodName('');
            assert.isFalse(result.success, 'Empty string should be rejected');
            assert.equal(result.error, 'EMPTY_INPUT', 'Should identify empty input');
        });
        it('should handle very long input strings', function () {
            var longInput = 'a'.repeat(10000);
            var result = ComponentName.methodName(longInput);
            // Validate based on expected behavior from MCP patterns
            if (result.success) {
                assert.isDefined(result.data, 'Long input processing should provide data');
            } else {
                assert.equal(result.error, 'INPUT_TOO_LONG', 'Should handle length limits');
            }
        });
        it('should handle special characters in input', function () {
            var specialChars = ['<script>', '&', '"quotes"', "'single'"];
            specialChars.forEach(function(input) {
                var result = ComponentName.methodName(input);
                // Validate security handling (from MCP security patterns)
                if (result.success) {
                    // Ensure output is sanitized
                    assert.notInclude(result.data.toString(), '<script>', 'Should sanitize script tags');
                }
            });
        });
    });
    describe('Security Tests (from MCP Security Patterns)', function () {
        it('should sanitize XSS attempts', function () {
            var xssAttempts = [
                '<script>alert("xss")</script>',
                'javascript:void(0)',
                'onload="alert(1)"'
            ];
            xssAttempts.forEach(function(xssInput) {
                var result = ComponentName.methodName(xssInput);
                if (result.success) {
                    // Ensure XSS is neutralized (pattern from MCP security guide)
                    assert.notInclude(result.data.toString().toLowerCase(), 'script', 'Should remove script tags');
                    assert.notInclude(result.data.toString().toLowerCase(), 'javascript:', 'Should remove javascript protocol');
                }
            });
        });
        it('should prevent SQL injection patterns', function () {
            var sqlAttempts = [
                "'; DROP TABLE users; --",
                "' OR '1'='1",
                "UNION SELECT * FROM admin"
            ];
            sqlAttempts.forEach(function(sqlInput) {
                var result = ComponentName.methodName(sqlInput);
                // Should either reject or sanitize (based on MCP security patterns)
                assert.isDefined(result, 'Should handle SQL injection attempts');
            });
        });
    });
    describe('Performance Tests', function () {
        it('should complete within reasonable time', function () {
            this.timeout(5000); // 5 second timeout
            var startTime = Date.now();
            var result = ComponentName.methodName('performance-test-input');
            var duration = Date.now() - startTime;
            assert.isBelow(duration, 1000, 'Should complete within 1 second');
            assert.isDefined(result, 'Should return result within time limit');
        });
        it('should handle multiple concurrent operations', function () {
            var promises = [];
            var concurrentCount = 10;
            for (var i = 0; i < concurrentCount; i++) {
                promises.push(
                    new Promise(function(resolve) {
                        var result = ComponentName.methodName('concurrent-test-' + i);
                        resolve(result);
                    })
                );
            }
            return Promise.all(promises).then(function(results) {
                assert.equal(results.length, concurrentCount, 'All operations should complete');
                results.forEach(function(result, index) {
                    assert.isDefined(result, 'Result ' + index + ' should be defined');
                });
            });
        });
    });
});
```
## Mock Patterns for SFCC Objects (from MCP Research)
```javascript
/**
 * Mock SFCC objects based on MCP system object research
 */
// Mock Product (use structure from MCP system object definitions)
var mockProduct = {
    ID: 'test-product-id',
    name: 'Test Product',
    online: true,
    custom: {
        customAttribute: 'test-value',
        customSize: 'Medium',
        customColor: 'Blue'
    },
    getPriceModel: function () {
        return {
            getPrice: function () {
                return {
                    value: 99.99,
                    currencyCode: 'USD'
                };
            }
        };
    },
    getAvailabilityModel: function() {
        return {
            isInStock: function() { return true; },
            getInventoryRecord: function() {
                return { allocation: { value: 100 } };
            }
        };
    }
};
// Mock Customer (use structure from MCP customer object research)
var mockCustomer = {
    authenticated: true,
    profile: {
        customerNo: 'test-customer-123',
        email: 'test@example.com',
        firstName: 'Test',
        lastName: 'User',
        custom: {
            loyaltyTier: 'Gold',
            customField: 'test-value'
        }
    },
    addressBook: {
        addresses: []
    }
};
// Mock Order (use structure from MCP order object research)
var mockOrder = {
    orderNo: 'TEST-ORDER-001',
    totalGrossPrice: { value: 199.99, currencyCode: 'USD' },
    productLineItems: [],
    customer: mockCustomer,
    custom: {
        orderSource: 'web',
        customOrderField: 'test-value'
    }
};
// Mock Site (for site preferences from MCP)
var mockSite = {
    current: {
        getCustomPreferenceValue: function(prefName) {
            var preferences = {
                'maxOrderQuantity': 10,
                'enableFeature': true,
                'customSetting': 'test-value'
            };
            return preferences[prefName] || null;
        }
    }
};
```
## Testing Checklist (MCP-Verified)
Before writing tests, verify with MCP:
- [ ] `mcp_sfcc-dev_search_best_practices` with query: "testing" - Get testing patterns
- [ ] `mcp_sfcc-dev_get_best_practice_guide` - Get implementation patterns for component
- [ ] `mcp_sfcc-dev_search_sfcc_classes` - Research classes being tested
Test coverage verification:
- [ ] Test both success and failure scenarios
- [ ] Mock external dependencies consistently
- [ ] Use descriptive test names that explain behavior
- [ ] Test error handling and edge cases
- [ ] Validate both positive and negative outcomes
- [ ] Include integration tests for complete workflows
- [ ] Test security validations and input sanitization
- [ ] Performance tests for critical operations
- [ ] Test with realistic SFCC object structures
## Advanced Testing Patterns (from MCP Best Practices)
```javascript
// Integration test pattern for SFCC workflows
describe('Integration: Complete Customer Journey', function() {
    it('should handle complete purchase flow', function() {
        // Test complete workflow using MCP-researched SFCC patterns
        var customerResult = ComponentName.authenticateCustomer(mockCustomer);
        assert.isTrue(customerResult.success, 'Customer authentication should succeed');
        var cartResult = ComponentName.addToCart(mockProduct, 2);
        assert.isTrue(cartResult.success, 'Add to cart should succeed');
        var orderResult = ComponentName.createOrder(cartResult.cart);
        assert.isTrue(orderResult.success, 'Order creation should succeed');
        assert.isDefined(orderResult.orderNo, 'Order number should be generated');
    });
});
// Transaction testing pattern (from MCP transaction patterns)
describe('Transaction Handling', function() {
    it('should handle transaction rollback on error', function() {
        var transactionRolledBack = false;
        mockDependencies['dw/system/Transaction'] = {
            wrap: function(callback) {
                try {
                    return callback();
                } catch (e) {
                    transactionRolledBack = true;
                    throw e;
                }
            }
        };
        // Test that triggers transaction rollback
        var ComponentWithTransaction = proxyquire('../../../cartridge/scripts/ComponentName', mockDependencies);
        assert.throws(function() {
            ComponentWithTransaction.methodThatShouldFail('error-input');
        });
        assert.isTrue(transactionRolledBack, 'Transaction should be rolled back on error');
    });
});
```
## NEVER Write Tests Without MCP
- ❌ Don't write tests without understanding patterns - use `mcp_sfcc-dev_search_best_practices`
- ❌ Don't mock SFCC objects without research - use `mcp_sfcc-dev_get_system_object_definitions`
- ❌ Don't test components without understanding implementation - use relevant MCP guides
- ❌ Don't assume SFCC class behavior - use `mcp_sfcc-dev_get_sfcc_class_info`