Real-World Examples & Case Studies
This guide provides practical examples of using SMock in real-world scenarios, including complete case studies from actual development projects.
Table of Contents
- Enterprise Application Testing
- Legacy Code Modernization
- Web API Testing
- File System Integration
- Database Access Layer Testing
- Third-Party Service Integration
- Microservices Testing
- Performance Critical Applications
Enterprise Application Testing
Case Study: Financial Trading System
Background: A financial trading system needed comprehensive testing of risk calculation modules that depend on external market data feeds and regulatory compliance checks.
Challenge: The system made extensive use of static methods for:
- Real-time market data retrieval
- Risk calculation utilities
- Compliance validation
- Audit logging
Solution with SMock:
[TestFixture]
public class TradingSystemTests
{
[Test]
public void Calculate_Portfolio_Risk_Under_Market_Stress()
{
// Arrange: Mock market data for stress testing
var stressTestData = new MarketData
{
SP500 = 3000, // 30% drop
VIX = 45, // High volatility
TreasuryYield = 0.5m,
LastUpdated = new DateTime(2024, 1, 15, 9, 30, 0)
};
using var marketDataMock = Mock.Setup(() =>
MarketDataProvider.GetCurrentData())
.Returns(stressTestData);
using var timeMock = Mock.Setup(() => DateTime.Now)
.Returns(new DateTime(2024, 1, 15, 9, 30, 0));
// Mock compliance rules for stress scenario
using var complianceMock = Mock.Setup(context =>
ComplianceValidator.ValidateRiskLimits(context.It.IsAny<decimal>()))
.Returns<decimal>(risk => new ComplianceResult
{
IsValid = risk <= 0.15m, // 15% max risk in stress conditions
Violations = risk > 0.15m ? new[] { "Exceeds stress test limits" } : new string[0]
});
// Mock audit logging to verify risk calculations are logged
var auditEntries = new List<AuditEntry>();
using var auditMock = Mock.Setup(context =>
AuditLogger.LogRiskCalculation(context.It.IsAny<string>(), context.It.IsAny<decimal>(), context.It.IsAny<DateTime>()))
.Callback<string, decimal, DateTime>((portfolioId, risk, timestamp) =>
auditEntries.Add(new AuditEntry
{
PortfolioId = portfolioId,
CalculatedRisk = risk,
Timestamp = timestamp
}));
// Act: Calculate risk for a test portfolio
var portfolio = new Portfolio
{
Id = "TEST_PORTFOLIO_001",
Positions = new[]
{
new Position { Symbol = "AAPL", Shares = 1000, Beta = 1.2m },
new Position { Symbol = "GOOGL", Shares = 500, Beta = 1.1m },
new Position { Symbol = "MSFT", Shares = 750, Beta = 0.9m }
}
};
var riskCalculator = new PortfolioRiskCalculator();
var riskReport = riskCalculator.CalculateRisk(portfolio);
// Assert: Verify risk calculation and compliance
Assert.IsNotNull(riskReport);
Assert.Greater(riskReport.VaR, 0, "Value at Risk should be positive in stress scenario");
Assert.Less(riskReport.VaR, 0.20m, "VaR should not exceed 20% even in stress conditions");
// Verify compliance check was performed
Assert.IsNotNull(riskReport.ComplianceStatus);
Assert.AreEqual(riskReport.VaR <= 0.15m, riskReport.ComplianceStatus.IsValid);
// Verify audit trail
Assert.AreEqual(1, auditEntries.Count);
Assert.AreEqual("TEST_PORTFOLIO_001", auditEntries[0].PortfolioId);
Assert.AreEqual(riskReport.VaR, auditEntries[0].CalculatedRisk);
}
[Test]
public void Validate_Trading_Hours_And_Market_Status()
{
// Test different market conditions and trading hours
var testScenarios = new[]
{
new { Time = new DateTime(2024, 1, 15, 9, 30, 0), IsOpen = true, Session = "Regular" },
new { Time = new DateTime(2024, 1, 15, 16, 0, 0), IsOpen = false, Session = "Closed" },
new { Time = new DateTime(2024, 1, 15, 4, 0, 0), IsOpen = true, Session = "PreMarket" },
new { Time = new DateTime(2024, 1, 13, 10, 0, 0), IsOpen = false, Session = "Weekend" } // Saturday
};
foreach (var scenario in testScenarios)
{
using var timeMock = Mock.Setup(() => DateTime.Now)
.Returns(scenario.Time);
using var marketStatusMock = Mock.Setup(() =>
MarketStatusProvider.GetCurrentStatus())
.Returns(new MarketStatus
{
IsOpen = scenario.IsOpen,
CurrentSession = scenario.Session,
NextOpenTime = scenario.IsOpen ? (DateTime?)null : GetNextMarketOpen(scenario.Time)
});
var tradingEngine = new TradingEngine();
var canTrade = tradingEngine.CanExecuteTrade();
Assert.AreEqual(scenario.IsOpen, canTrade,
$"Trading should be {(scenario.IsOpen ? "allowed" : "blocked")} during {scenario.Session} at {scenario.Time}");
}
}
private DateTime GetNextMarketOpen(DateTime currentTime)
{
// Logic to calculate next market opening time
if (currentTime.DayOfWeek == DayOfWeek.Saturday)
return currentTime.AddDays(2).Date.AddHours(9.5); // Monday 9:30 AM
if (currentTime.DayOfWeek == DayOfWeek.Sunday)
return currentTime.AddDays(1).Date.AddHours(9.5); // Monday 9:30 AM
if (currentTime.TimeOfDay > new TimeSpan(16, 0, 0))
return currentTime.AddDays(1).Date.AddHours(9.5); // Next day 9:30 AM
return currentTime.Date.AddHours(9.5); // Today 9:30 AM
}
}
Results: The SMock-based testing approach enabled comprehensive testing of the trading system's risk calculations across various market conditions, reducing production incidents by 75% and ensuring regulatory compliance.
Legacy Code Modernization
Case Study: Modernizing a 15-Year-Old Inventory Management System
Background: A manufacturing company needed to modernize their inventory management system that was heavily dependent on static utility classes and file-based configuration.
Challenge: The legacy code had:
- Deeply nested static method calls
- File system dependencies for configuration
- Hard-coded paths and system dependencies
- No existing unit tests
SMock-Enabled Modernization Strategy:
// Legacy code structure (simplified)
public class LegacyInventoryManager
{
public InventoryReport GenerateReport(DateTime reportDate)
{
// Legacy code with multiple static dependencies
var configPath = SystemPaths.GetConfigDirectory();
var config = FileHelper.ReadConfig(Path.Combine(configPath, "inventory.config"));
var dbConnection = DatabaseFactory.CreateConnection(config.ConnectionString);
var warehouseData = DatabaseQueryHelper.ExecuteQuery(
dbConnection,
SqlQueryBuilder.BuildWarehouseQuery(reportDate)
);
var report = ReportFormatter.FormatInventoryData(warehouseData);
AuditLogger.LogReportGeneration("Inventory", reportDate, Environment.UserName);
return report;
}
}
// Modern test-enabled version with SMock
[TestFixture]
public class ModernizedInventoryManagerTests
{
[Test]
public void Generate_Inventory_Report_Success_Scenario()
{
// Arrange: Mock the complex legacy dependencies
var testDate = new DateTime(2024, 1, 15);
var testUser = "TestUser";
var testConfig = new InventoryConfig
{
ConnectionString = "Server=localhost;Database=TestInventory;",
WarehouseLocations = new[] { "WH001", "WH002", "WH003" },
ReportFormats = new[] { "Summary", "Detailed" }
};
using var pathMock = Mock.Setup(() => SystemPaths.GetConfigDirectory())
.Returns(@"C:\TestConfig");
using var fileMock = Mock.Setup(() =>
FileHelper.ReadConfig(@"C:\TestConfig\inventory.config"))
.Returns(testConfig);
using var dbFactoryMock = Mock.Setup(() =>
DatabaseFactory.CreateConnection(testConfig.ConnectionString))
.Returns(new MockDbConnection { IsConnected = true });
using var queryBuilderMock = Mock.Setup(() =>
SqlQueryBuilder.BuildWarehouseQuery(testDate))
.Returns("SELECT * FROM Inventory WHERE ReportDate = '2024-01-15'");
var mockWarehouseData = new[]
{
new WarehouseItem { Location = "WH001", ItemCode = "ITEM001", Quantity = 150 },
new WarehouseItem { Location = "WH002", ItemCode = "ITEM002", Quantity = 200 },
new WarehouseItem { Location = "WH003", ItemCode = "ITEM003", Quantity = 75 }
};
using var queryMock = Mock.Setup(context =>
DatabaseQueryHelper.ExecuteQuery(context.It.IsAny<IDbConnection>(), context.It.IsAny<string>()))
.Returns(mockWarehouseData);
using var formatterMock = Mock.Setup(() =>
ReportFormatter.FormatInventoryData(mockWarehouseData))
.Returns(new InventoryReport
{
ReportDate = testDate,
TotalItems = 3,
TotalQuantity = 425,
WarehouseBreakdown = mockWarehouseData.GroupBy(w => w.Location)
.ToDictionary(g => g.Key, g => g.Sum(w => w.Quantity))
});
using var userMock = Mock.Setup(() => Environment.UserName)
.Returns(testUser);
var auditEntries = new List<AuditLogEntry>();
using var auditMock = Mock.Setup(context =>
AuditLogger.LogReportGeneration(context.It.IsAny<string>(), context.It.IsAny<DateTime>(), context.It.IsAny<string>()))
.Callback<string, DateTime, string>((reportType, date, user) =>
auditEntries.Add(new AuditLogEntry
{
ReportType = reportType,
GenerationDate = date,
User = user,
Timestamp = DateTime.Now
}));
// Act: Generate the report using the legacy system
var inventoryManager = new LegacyInventoryManager();
var report = inventoryManager.GenerateReport(testDate);
// Assert: Verify the report and all interactions
Assert.IsNotNull(report);
Assert.AreEqual(testDate, report.ReportDate);
Assert.AreEqual(3, report.TotalItems);
Assert.AreEqual(425, report.TotalQuantity);
// Verify warehouse breakdown
Assert.AreEqual(150, report.WarehouseBreakdown["WH001"]);
Assert.AreEqual(200, report.WarehouseBreakdown["WH002"]);
Assert.AreEqual(75, report.WarehouseBreakdown["WH003"]);
// Verify audit logging
Assert.AreEqual(1, auditEntries.Count);
Assert.AreEqual("Inventory", auditEntries[0].ReportType);
Assert.AreEqual(testDate, auditEntries[0].GenerationDate);
Assert.AreEqual(testUser, auditEntries[0].User);
}
[Test]
public void Handle_Database_Connection_Failure_Gracefully()
{
// Arrange: Simulate database connection failure
var testDate = new DateTime(2024, 1, 15);
using var pathMock = Mock.Setup(() => SystemPaths.GetConfigDirectory())
.Returns(@"C:\TestConfig");
using var fileMock = Mock.Setup(context =>
FileHelper.ReadConfig(context.It.IsAny<string>()))
.Returns(new InventoryConfig { ConnectionString = "invalid_connection" });
// Mock database connection failure
using var dbFailureMock = Mock.Setup(() =>
DatabaseFactory.CreateConnection("invalid_connection"))
.Throws<DatabaseConnectionException>();
var inventoryManager = new LegacyInventoryManager();
// Act & Assert: Verify graceful failure handling
var exception = Assert.Throws<DatabaseConnectionException>(
() => inventoryManager.GenerateReport(testDate));
Assert.IsNotNull(exception);
// Verify that the system fails fast and doesn't attempt further operations
}
[Test]
public void Validate_Configuration_File_Processing()
{
// Test various configuration scenarios
var configScenarios = new[]
{
new { Scenario = "Missing config file", ShouldThrow = true, Config = (InventoryConfig)null },
new { Scenario = "Empty connection string", ShouldThrow = true, Config = new InventoryConfig { ConnectionString = "" } },
new { Scenario = "No warehouse locations", ShouldThrow = false, Config = new InventoryConfig { ConnectionString = "valid", WarehouseLocations = new string[0] } },
new { Scenario = "Valid configuration", ShouldThrow = false, Config = new InventoryConfig { ConnectionString = "valid", WarehouseLocations = new[] { "WH001" } } }
};
foreach (var scenario in configScenarios)
{
using var pathMock = Mock.Setup(() => SystemPaths.GetConfigDirectory())
.Returns(@"C:\TestConfig");
if (scenario.Config == null)
{
using var fileMock = Mock.Setup(context =>
FileHelper.ReadConfig(context.It.IsAny<string>()))
.Throws<FileNotFoundException>();
}
else
{
using var fileMock = Mock.Setup(context =>
FileHelper.ReadConfig(context.It.IsAny<string>()))
.Returns(scenario.Config);
if (!string.IsNullOrEmpty(scenario.Config.ConnectionString))
{
using var dbMock = Mock.Setup(() =>
DatabaseFactory.CreateConnection(scenario.Config.ConnectionString))
.Returns(new MockDbConnection { IsConnected = true });
}
}
var inventoryManager = new LegacyInventoryManager();
if (scenario.ShouldThrow)
{
Assert.Throws<Exception>(() =>
inventoryManager.GenerateReport(new DateTime(2024, 1, 15)),
$"Scenario '{scenario.Scenario}' should throw an exception");
}
else
{
// Additional mocks needed for successful scenarios
using var queryBuilderMock = Mock.Setup(context =>
SqlQueryBuilder.BuildWarehouseQuery(context.It.IsAny<DateTime>()))
.Returns("SELECT * FROM Inventory");
using var queryMock = Mock.Setup(context =>
DatabaseQueryHelper.ExecuteQuery(context.It.IsAny<IDbConnection>(), context.It.IsAny<string>()))
.Returns(new WarehouseItem[0]);
using var formatterMock = Mock.Setup(context =>
ReportFormatter.FormatInventoryData(context.It.IsAny<WarehouseItem[]>()))
.Returns(new InventoryReport
{
ReportDate = new DateTime(2024, 1, 15),
TotalItems = 0,
TotalQuantity = 0
});
using var auditMock = Mock.Setup(context =>
AuditLogger.LogReportGeneration(context.It.IsAny<string>(), context.It.IsAny<DateTime>(), context.It.IsAny<string>()));
Assert.DoesNotThrow(() =>
inventoryManager.GenerateReport(new DateTime(2024, 1, 15)),
$"Scenario '{scenario.Scenario}' should not throw an exception");
}
}
}
}
Results: The modernization project was completed 40% faster than estimated due to SMock enabling comprehensive testing of the legacy code without modification. This allowed for confident refactoring and gradual modernization.
Web API Testing
Case Study: E-commerce API with External Dependencies
Background: An e-commerce platform's order processing API needed thorough testing of payment processing, inventory management, and shipping calculations.
[TestFixture]
public class OrderProcessingApiTests
{
[Test]
public async Task Process_Order_Complete_Success_Flow()
{
// Arrange: Set up comprehensive mocking for order processing
var testOrder = new OrderRequest
{
CustomerId = "CUST_12345",
Items = new[]
{
new OrderItem { ProductId = "PROD_001", Quantity = 2, Price = 29.99m },
new OrderItem { ProductId = "PROD_002", Quantity = 1, Price = 49.99m }
},
ShippingAddress = new Address
{
Street = "123 Test St",
City = "Test City",
State = "TS",
ZipCode = "12345",
Country = "US"
},
PaymentMethod = new PaymentMethod
{
Type = "CreditCard",
Token = "tok_test_12345"
}
};
// Mock inventory checks
using var inventoryMock = Mock.Setup(context =>
InventoryService.CheckAvailability(context.It.IsAny<string>(), context.It.IsAny<int>()))
.Returns<string, int>((productId, quantity) => new InventoryResult
{
ProductId = productId,
Available = true,
StockLevel = quantity + 10 // Always have more than requested
});
// Mock pricing service
using var pricingMock = Mock.Setup(context =>
PricingService.CalculateTotal(context.It.IsAny<OrderItem[]>()))
.Returns(new PricingResult
{
Subtotal = 109.97m,
Tax = 8.80m,
Total = 118.77m
});
// Mock shipping calculation
using var shippingMock = Mock.Setup(context =>
ShippingCalculator.CalculateShipping(context.It.IsAny<Address>(), context.It.IsAny<decimal>()))
.Returns(new ShippingResult
{
Cost = 9.99m,
EstimatedDays = 3,
Method = "Standard"
});
// Mock payment processing
using var paymentMock = Mock.Setup(context =>
PaymentProcessor.ProcessPayment(context.It.IsAny<PaymentMethod>(), context.It.IsAny<decimal>()))
.Returns(Task.FromResult(new PaymentResult
{
Success = true,
TransactionId = "TXN_789012",
AuthorizationCode = "AUTH_345678"
}));
// Mock order persistence
var savedOrders = new List<Order>();
using var orderSaveMock = Mock.Setup(context =>
OrderRepository.SaveOrder(context.It.IsAny<Order>()))
.Callback<Order>(order => savedOrders.Add(order))
.Returns(Task.FromResult("ORD_" + Guid.NewGuid().ToString("N")[..8].ToUpper()));
// Mock notification service
var sentNotifications = new List<NotificationRequest>();
using var notificationMock = Mock.Setup(context =>
NotificationService.SendOrderConfirmation(context.It.IsAny<string>(), context.It.IsAny<string>()))
.Callback<string, string>((customerId, orderId) =>
sentNotifications.Add(new NotificationRequest
{
CustomerId = customerId,
OrderId = orderId,
Type = "OrderConfirmation"
}))
.Returns(Task.CompletedTask);
// Mock audit logging
var auditLogs = new List<AuditLog>();
using var auditMock = Mock.Setup(context =>
AuditLogger.LogOrderProcessing(context.It.IsAny<string>(), context.It.IsAny<string>(), context.It.IsAny<decimal>()))
.Callback<string, string, decimal>((orderId, customerId, amount) =>
auditLogs.Add(new AuditLog
{
OrderId = orderId,
CustomerId = customerId,
Amount = amount,
Action = "OrderProcessed",
Timestamp = DateTime.UtcNow
}));
// Act: Process the order
var orderProcessor = new OrderProcessor();
var result = await orderProcessor.ProcessOrderAsync(testOrder);
// Assert: Verify complete order processing
Assert.IsNotNull(result);
Assert.IsTrue(result.Success);
Assert.IsNotNull(result.OrderId);
Assert.AreEqual("TXN_789012", result.TransactionId);
// Verify order was saved correctly
Assert.AreEqual(1, savedOrders.Count);
var savedOrder = savedOrders[0];
Assert.AreEqual(testOrder.CustomerId, savedOrder.CustomerId);
Assert.AreEqual(2, savedOrder.Items.Count);
Assert.AreEqual(118.77m + 9.99m, savedOrder.Total); // Total + Shipping
// Verify notification was sent
Assert.AreEqual(1, sentNotifications.Count);
Assert.AreEqual(testOrder.CustomerId, sentNotifications[0].CustomerId);
// Verify audit logging
Assert.AreEqual(1, auditLogs.Count);
Assert.AreEqual(testOrder.CustomerId, auditLogs[0].CustomerId);
Assert.AreEqual(savedOrder.Total, auditLogs[0].Amount);
}
[Test]
public async Task Handle_Payment_Failure_Gracefully()
{
// Arrange: Set up scenario where payment fails
var testOrder = CreateBasicOrderRequest();
// Set up successful mocks for everything except payment
SetupSuccessfulInventoryAndPricing();
// Mock payment failure
using var paymentMock = Mock.Setup(context =>
PaymentProcessor.ProcessPayment(context.It.IsAny<PaymentMethod>(), context.It.IsAny<decimal>()))
.Returns(Task.FromResult(new PaymentResult
{
Success = false,
ErrorCode = "DECLINED",
ErrorMessage = "Insufficient funds"
}));
// Mock inventory rollback
var rollbackCalls = new List<InventoryRollbackRequest>();
using var rollbackMock = Mock.Setup(context =>
InventoryService.RollbackReservation(context.It.IsAny<string>(), context.It.IsAny<int>()))
.Callback<string, int>((productId, quantity) =>
rollbackCalls.Add(new InventoryRollbackRequest
{
ProductId = productId,
Quantity = quantity
}));
// Ensure no order is saved on failure
using var orderSaveMock = Mock.Setup(context =>
OrderRepository.SaveOrder(context.It.IsAny<Order>()))
.Throws(new InvalidOperationException("Order should not be saved on payment failure"));
// Act: Process order with failing payment
var orderProcessor = new OrderProcessor();
var result = await orderProcessor.ProcessOrderAsync(testOrder);
// Assert: Verify failure handling
Assert.IsNotNull(result);
Assert.IsFalse(result.Success);
Assert.AreEqual("DECLINED", result.ErrorCode);
Assert.AreEqual("Insufficient funds", result.ErrorMessage);
// Verify inventory was rolled back
Assert.AreEqual(2, rollbackCalls.Count); // One for each item
Assert.IsTrue(rollbackCalls.Any(r => r.ProductId == "PROD_001" && r.Quantity == 2));
Assert.IsTrue(rollbackCalls.Any(r => r.ProductId == "PROD_002" && r.Quantity == 1));
}
[Test]
public async Task Handle_Inventory_Shortage_Properly()
{
// Arrange: Set up scenario with insufficient inventory
var testOrder = CreateBasicOrderRequest();
// Mock partial inventory availability
using var inventoryMock = Mock.Setup(context =>
InventoryService.CheckAvailability(context.It.IsAny<string>(), context.It.IsAny<int>()))
.Returns<string, int>((productId, quantity) =>
{
if (productId == "PROD_001")
return new InventoryResult { ProductId = productId, Available = true, StockLevel = quantity };
else
return new InventoryResult { ProductId = productId, Available = false, StockLevel = 0 };
});
// Act: Process order with inventory shortage
var orderProcessor = new OrderProcessor();
var result = await orderProcessor.ProcessOrderAsync(testOrder);
// Assert: Verify proper handling of inventory shortage
Assert.IsNotNull(result);
Assert.IsFalse(result.Success);
Assert.AreEqual("INVENTORY_INSUFFICIENT", result.ErrorCode);
Assert.Contains("PROD_002", result.ErrorMessage);
}
private OrderRequest CreateBasicOrderRequest()
{
return new OrderRequest
{
CustomerId = "CUST_12345",
Items = new[]
{
new OrderItem { ProductId = "PROD_001", Quantity = 2, Price = 29.99m },
new OrderItem { ProductId = "PROD_002", Quantity = 1, Price = 49.99m }
},
ShippingAddress = new Address
{
Street = "123 Test St",
City = "Test City",
State = "TS",
ZipCode = "12345",
Country = "US"
},
PaymentMethod = new PaymentMethod
{
Type = "CreditCard",
Token = "tok_test_12345"
}
};
}
private void SetupSuccessfulInventoryAndPricing()
{
Mock.Setup(context => InventoryService.CheckAvailability(context.It.IsAny<string>(), context.It.IsAny<int>()))
.Returns<string, int>((productId, quantity) => new InventoryResult
{
ProductId = productId,
Available = true,
StockLevel = quantity + 10
});
Mock.Setup(context => PricingService.CalculateTotal(context.It.IsAny<OrderItem[]>()))
.Returns(new PricingResult { Subtotal = 109.97m, Tax = 8.80m, Total = 118.77m });
Mock.Setup(context => ShippingCalculator.CalculateShipping(context.It.IsAny<Address>(), context.It.IsAny<decimal>()))
.Returns(new ShippingResult { Cost = 9.99m, EstimatedDays = 3, Method = "Standard" });
}
}
File System Integration
Case Study: Document Management System
[TestFixture]
public class DocumentManagementSystemTests
{
[Test]
public void Upload_Document_With_Virus_Scanning_And_Metadata_Extraction()
{
// Arrange: Complex document processing pipeline
var testDocument = new DocumentUploadRequest
{
FileName = "important_contract.pdf",
Content = Convert.FromBase64String("JVBERi0xLjQKJcOkw7zDt..."), // Mock PDF content
UserId = "USER_12345",
Category = "Contracts"
};
var expectedPath = Path.Combine(@"C:\Documents\Contracts\2024\01", "important_contract.pdf");
// Mock file system operations
using var directoryExistsMock = Mock.Setup(() =>
Directory.Exists(Path.GetDirectoryName(expectedPath)))
.Returns(false);
using var directoryCreateMock = Mock.Setup(() =>
Directory.CreateDirectory(Path.GetDirectoryName(expectedPath)));
using var fileWriteMock = Mock.Setup(context =>
File.WriteAllBytes(expectedPath, context.It.IsAny<byte[]>()));
// Mock virus scanning
using var virusScanMock = Mock.Setup(() =>
VirusScanner.ScanFile(expectedPath))
.Returns(new ScanResult
{
IsClean = true,
ScanDuration = TimeSpan.FromSeconds(2.3),
Engine = "ClamAV",
SignatureVersion = "2024.01.15"
});
// Mock metadata extraction
using var metadataExtractionMock = Mock.Setup(() =>
MetadataExtractor.ExtractMetadata(expectedPath))
.Returns(new DocumentMetadata
{
Title = "Service Agreement Contract",
Author = "Legal Department",
CreationDate = new DateTime(2024, 1, 10),
PageCount = 15,
FileSize = testDocument.Content.Length,
MimeType = "application/pdf"
});
// Mock thumbnail generation
using var thumbnailMock = Mock.Setup(context =>
ThumbnailGenerator.GenerateThumbnail(expectedPath, context.It.IsAny<ThumbnailOptions>()))
.Returns(new ThumbnailResult
{
ThumbnailPath = expectedPath.Replace(".pdf", "_thumb.jpg"),
Width = 200,
Height = 260
});
// Mock database storage
var savedDocuments = new List<DocumentRecord>();
using var dbSaveMock = Mock.Setup(context =>
DocumentRepository.SaveDocument(context.It.IsAny<DocumentRecord>()))
.Callback<DocumentRecord>(doc => savedDocuments.Add(doc))
.Returns(Task.FromResult("DOC_" + Guid.NewGuid().ToString("N")[..8]));
// Mock audit logging
var auditEntries = new List<DocumentAuditEntry>();
using var auditMock = Mock.Setup(context =>
DocumentAuditLogger.LogDocumentUpload(context.It.IsAny<string>(), context.It.IsAny<string>(), context.It.IsAny<long>()))
.Callback<string, string, long>((userId, fileName, fileSize) =>
auditEntries.Add(new DocumentAuditEntry
{
UserId = userId,
FileName = fileName,
FileSize = fileSize,
Action = "Upload",
Timestamp = DateTime.UtcNow
}));
// Act: Upload the document
var documentManager = new DocumentManager();
var result = await documentManager.UploadDocumentAsync(testDocument);
// Assert: Verify complete upload process
Assert.IsNotNull(result);
Assert.IsTrue(result.Success);
Assert.IsNotNull(result.DocumentId);
// Verify document was saved to database
Assert.AreEqual(1, savedDocuments.Count);
var savedDoc = savedDocuments[0];
Assert.AreEqual(testDocument.FileName, savedDoc.FileName);
Assert.AreEqual(testDocument.UserId, savedDoc.UploadedBy);
Assert.AreEqual(testDocument.Category, savedDoc.Category);
Assert.AreEqual("Service Agreement Contract", savedDoc.Metadata.Title);
Assert.AreEqual(15, savedDoc.Metadata.PageCount);
// Verify audit trail
Assert.AreEqual(1, auditEntries.Count);
Assert.AreEqual(testDocument.UserId, auditEntries[0].UserId);
Assert.AreEqual(testDocument.FileName, auditEntries[0].FileName);
}
[Test]
public void Handle_Virus_Detection_During_Upload()
{
// Arrange: Infected file scenario
var infectedDocument = new DocumentUploadRequest
{
FileName = "suspicious_file.exe",
Content = new byte[] { 0x4D, 0x5A, 0x90, 0x00 }, // PE header
UserId = "USER_12345",
Category = "Uploads"
};
var tempPath = Path.Combine(Path.GetTempPath(), "suspicious_file.exe");
// Set up file system mocks
using var directoryExistsMock = Mock.Setup(context =>
Directory.Exists(context.It.IsAny<string>())).Returns(true);
using var fileWriteMock = Mock.Setup(context =>
File.WriteAllBytes(tempPath, context.It.IsAny<byte[]>()));
// Mock virus detection
using var virusScanMock = Mock.Setup(() =>
VirusScanner.ScanFile(tempPath))
.Returns(new ScanResult
{
IsClean = false,
ThreatName = "Win32.Malware.Generic",
ScanDuration = TimeSpan.FromSeconds(1.2)
});
// Mock quarantine process
var quarantinedFiles = new List<QuarantineRecord>();
using var quarantineMock = Mock.Setup(context =>
QuarantineManager.QuarantineFile(tempPath, context.It.IsAny<string>()))
.Callback<string, string>((filePath, threatName) =>
quarantinedFiles.Add(new QuarantineRecord
{
OriginalPath = filePath,
ThreatName = threatName,
QuarantineTime = DateTime.UtcNow
}));
// Mock file deletion
using var fileDeleteMock = Mock.Setup(() => File.Delete(tempPath));
// Mock security incident logging
var securityIncidents = new List<SecurityIncident>();
using var securityLogMock = Mock.Setup(context =>
SecurityLogger.LogVirusDetection(context.It.IsAny<string>(), context.It.IsAny<string>(), context.It.IsAny<string>()))
.Callback<string, string, string>((userId, fileName, threatName) =>
securityIncidents.Add(new SecurityIncident
{
UserId = userId,
FileName = fileName,
ThreatName = threatName,
Severity = "High",
Timestamp = DateTime.UtcNow
}));
// Act: Attempt to upload infected file
var documentManager = new DocumentManager();
var result = await documentManager.UploadDocumentAsync(infectedDocument);
// Assert: Verify security response
Assert.IsNotNull(result);
Assert.IsFalse(result.Success);
Assert.AreEqual("VIRUS_DETECTED", result.ErrorCode);
Assert.Contains("Win32.Malware.Generic", result.ErrorMessage);
// Verify file was quarantined
Assert.AreEqual(1, quarantinedFiles.Count);
Assert.AreEqual("Win32.Malware.Generic", quarantinedFiles[0].ThreatName);
// Verify security incident was logged
Assert.AreEqual(1, securityIncidents.Count);
Assert.AreEqual(infectedDocument.UserId, securityIncidents[0].UserId);
Assert.AreEqual("High", securityIncidents[0].Severity);
}
}
Database Access Layer Testing
Case Study: Multi-Tenant SaaS Application
[TestFixture]
public class MultiTenantDataAccessTests
{
[Test]
public void Ensure_Tenant_Isolation_In_Data_Queries()
{
// Arrange: Multi-tenant scenario testing
var tenant1Id = "TENANT_A";
var tenant2Id = "TENANT_B";
var currentUser = "USER_123";
// Mock tenant context
using var tenantContextMock = Mock.Setup(() =>
TenantContext.GetCurrentTenantId())
.Returns(tenant1Id);
using var userContextMock = Mock.Setup(() =>
UserContext.GetCurrentUserId())
.Returns(currentUser);
// Mock database connection with tenant isolation
var executedQueries = new List<DatabaseQuery>();
using var dbQueryMock = Mock.Setup(context =>
DatabaseExecutor.ExecuteQuery(context.It.IsAny<string>(), context.It.IsAny<object[]>()))
.Callback<string, object[]>((sql, parameters) =>
executedQueries.Add(new DatabaseQuery
{
Sql = sql,
Parameters = parameters,
TenantId = TenantContext.GetCurrentTenantId(),
ExecutedAt = DateTime.UtcNow
}))
.Returns(new[]
{
new Customer { Id = 1, Name = "Customer A1", TenantId = tenant1Id },
new Customer { Id = 2, Name = "Customer A2", TenantId = tenant1Id }
});
// Mock audit logging for data access
var dataAccessLogs = new List<DataAccessAuditEntry>();
using var dataAuditMock = Mock.Setup(context =>
DataAccessAuditor.LogQuery(context.It.IsAny<string>(), context.It.IsAny<string>(), context.It.IsAny<string>()))
.Callback<string, string, string>((userId, tenantId, operation) =>
dataAccessLogs.Add(new DataAccessAuditEntry
{
UserId = userId,
TenantId = tenantId,
Operation = operation,
Timestamp = DateTime.UtcNow
}));
// Act: Execute tenant-specific query
var customerRepository = new CustomerRepository();
var customers = customerRepository.GetActiveCustomers();
// Assert: Verify tenant isolation
Assert.IsNotNull(customers);
Assert.AreEqual(2, customers.Count());
Assert.IsTrue(customers.All(c => c.TenantId == tenant1Id));
// Verify query was properly formed with tenant filter
Assert.AreEqual(1, executedQueries.Count);
var executedQuery = executedQueries[0];
Assert.Contains("TenantId = @tenantId", executedQuery.Sql);
Assert.Contains(tenant1Id, executedQuery.Parameters);
// Verify data access was audited
Assert.AreEqual(1, dataAccessLogs.Count);
Assert.AreEqual(currentUser, dataAccessLogs[0].UserId);
Assert.AreEqual(tenant1Id, dataAccessLogs[0].TenantId);
Assert.AreEqual("GetActiveCustomers", dataAccessLogs[0].Operation);
}
[Test]
public void Prevent_Cross_Tenant_Data_Access()
{
// Arrange: Attempt to access data from different tenant
var currentTenantId = "TENANT_A";
var attemptedCustomerId = 999; // Belongs to TENANT_B
using var tenantContextMock = Mock.Setup(() =>
TenantContext.GetCurrentTenantId())
.Returns(currentTenantId);
// Mock database query that finds no results (due to tenant filtering)
using var dbQueryMock = Mock.Setup(context =>
DatabaseExecutor.ExecuteQuery(context.It.IsAny<string>(), context.It.IsAny<object[]>()))
.Returns(new Customer[0]); // No results due to tenant isolation
// Mock security violation logging
var securityViolations = new List<SecurityViolation>();
using var securityMock = Mock.Setup(context =>
SecurityLogger.LogUnauthorizedAccess(context.It.IsAny<string>(), context.It.IsAny<string>(), context.It.IsAny<string>()))
.Callback<string, string, string>((userId, resource, reason) =>
securityViolations.Add(new SecurityViolation
{
UserId = userId,
AttemptedResource = resource,
Reason = reason,
Timestamp = DateTime.UtcNow
}));
// Act: Attempt to access cross-tenant data
var customerRepository = new CustomerRepository();
var customer = customerRepository.GetCustomerById(attemptedCustomerId);
// Assert: Verify access was denied
Assert.IsNull(customer);
// In a production scenario, this might trigger additional security logging
// if the application detects cross-tenant access attempts
}
}
This comprehensive guide demonstrates how SMock enables testing of complex, real-world applications with multiple external dependencies, ensuring reliable and maintainable test suites.
Working Real-World Examples
The real-world examples shown in this guide are based on actual working test cases. You can find complete, debugged examples in the SMock test suite:
- Enterprise Scenarios -
src/StaticMock.Tests/Tests/Examples/RealWorldExamples/EnterpriseScenarios.cs
These examples demonstrate:
- Financial trading system - Risk calculation with market data mocking and compliance validation
- Legacy code modernization - File processing with complex dependency chains
- Web API order processing - Complete e-commerce flow with payment, inventory, and shipping
- Document management - File system operations with virus scanning and metadata extraction
- Multi-tenant data access - Database isolation and security testing
Running Real-World Examples
# Navigate to the src directory
cd src
# Run the real-world examples specifically
dotnet test --filter "ClassName=EnterpriseScenarios"
# Or run all example tests
dotnet test --filter "FullyQualifiedName~Examples"