---
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`