How can I simulate different network conditions with Puppeteer-Sharp?
Simulating different network conditions is essential for testing web applications under various connectivity scenarios. Puppeteer-Sharp provides powerful network emulation capabilities that allow developers to simulate slow connections, high latency, and offline conditions to ensure their applications perform well across different network environments.
Understanding Network Emulation in Puppeteer-Sharp
Network emulation in Puppeteer-Sharp leverages the Chrome DevTools Protocol to modify network behavior at the browser level. This allows you to test how your web application behaves under different network conditions without actually changing your internet connection.
Setting Up Network Emulation
Basic Network Throttling
The primary method for simulating network conditions is through the Page.EmulateNetworkConditionsAsync()
method:
using PuppeteerSharp;
var launchOptions = new LaunchOptions
{
Headless = false,
Args = new[] { "--disable-web-security", "--disable-features=VizDisplayCompositor" }
};
using var browser = await Puppeteer.LaunchAsync(launchOptions);
using var page = await browser.NewPageAsync();
// Simulate slow 3G network conditions
await page.EmulateNetworkConditionsAsync(new NetworkConditions
{
Offline = false,
DownloadThroughput = 1.5 * 1024 * 1024 / 8, // 1.5 Mbps in bytes per second
UploadThroughput = 750 * 1024 / 8, // 750 Kbps in bytes per second
Latency = 40 // 40ms latency
});
await page.GoToAsync("https://example.com");
Predefined Network Profiles
You can create predefined network profiles for common scenarios:
public static class NetworkProfiles
{
public static NetworkConditions Slow3G => new()
{
Offline = false,
DownloadThroughput = 500 * 1024 / 8, // 500 Kbps
UploadThroughput = 500 * 1024 / 8, // 500 Kbps
Latency = 2000 // 2000ms latency
};
public static NetworkConditions Fast3G => new()
{
Offline = false,
DownloadThroughput = 1.6 * 1024 * 1024 / 8, // 1.6 Mbps
UploadThroughput = 750 * 1024 / 8, // 750 Kbps
Latency = 150 // 150ms latency
};
public static NetworkConditions Regular4G => new()
{
Offline = false,
DownloadThroughput = 4 * 1024 * 1024 / 8, // 4 Mbps
UploadThroughput = 3 * 1024 * 1024 / 8, // 3 Mbps
Latency = 20 // 20ms latency
};
public static NetworkConditions Offline => new()
{
Offline = true,
DownloadThroughput = 0,
UploadThroughput = 0,
Latency = 0
};
}
// Usage example
await page.EmulateNetworkConditionsAsync(NetworkProfiles.Slow3G);
Advanced Network Simulation Scenarios
Testing Progressive Loading
Simulate how your application handles progressive loading under different network conditions:
public async Task TestProgressiveLoadingAsync()
{
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = false });
using var page = await browser.NewPageAsync();
// Start with fast connection
await page.EmulateNetworkConditionsAsync(NetworkProfiles.Fast3G);
// Navigate and measure initial load
var stopwatch = Stopwatch.StartNew();
await page.GoToAsync("https://example.com");
var initialLoadTime = stopwatch.ElapsedMilliseconds;
Console.WriteLine($"Initial load time (Fast 3G): {initialLoadTime}ms");
// Switch to slow connection and test subsequent interactions
await page.EmulateNetworkConditionsAsync(NetworkProfiles.Slow3G);
stopwatch.Restart();
await page.ClickAsync("#load-more-content");
await page.WaitForSelectorAsync(".new-content", new WaitForSelectorOptions
{
Timeout = 30000
});
var slowLoadTime = stopwatch.ElapsedMilliseconds;
Console.WriteLine($"Content load time (Slow 3G): {slowLoadTime}ms");
}
Simulating Intermittent Connectivity
Test how your application handles network interruptions:
public async Task TestIntermittentConnectivityAsync()
{
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = false });
using var page = await browser.NewPageAsync();
await page.GoToAsync("https://example.com");
// Simulate network interruption
await page.EmulateNetworkConditionsAsync(NetworkProfiles.Offline);
// Try to perform an action that requires network
try
{
await page.ClickAsync("#submit-form");
await page.WaitForSelectorAsync(".success-message",
new WaitForSelectorOptions { Timeout = 5000 });
}
catch (WaitTaskTimeoutException)
{
Console.WriteLine("Action failed due to network interruption - as expected");
}
// Restore connectivity
await page.EmulateNetworkConditionsAsync(NetworkProfiles.Fast3G);
// Retry the action
await page.ClickAsync("#retry-button");
await page.WaitForSelectorAsync(".success-message");
Console.WriteLine("Action succeeded after connectivity restored");
}
Monitoring Network Performance
When tracking network requests similar to monitoring network requests in Puppeteer, you can combine network emulation with request monitoring to analyze performance:
public async Task MonitorNetworkPerformanceAsync()
{
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = false });
using var page = await browser.NewPageAsync();
var networkRequests = new List<Request>();
var networkResponses = new List<Response>();
page.Request += (sender, e) => networkRequests.Add(e.Request);
page.Response += (sender, e) => networkResponses.Add(e.Response);
// Test under different network conditions
var conditions = new[]
{
("Fast 3G", NetworkProfiles.Fast3G),
("Slow 3G", NetworkProfiles.Slow3G),
("Regular 4G", NetworkProfiles.Regular4G)
};
foreach (var (name, condition) in conditions)
{
await page.EmulateNetworkConditionsAsync(condition);
var stopwatch = Stopwatch.StartNew();
await page.ReloadAsync();
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
stopwatch.Stop();
Console.WriteLine($"{name}: {stopwatch.ElapsedMilliseconds}ms, " +
$"Requests: {networkRequests.Count}, " +
$"Failed: {networkResponses.Count(r => !r.Ok)}");
networkRequests.Clear();
networkResponses.Clear();
}
}
Testing Mobile Network Conditions
Combine network emulation with mobile device emulation for comprehensive mobile testing:
public async Task TestMobileNetworkConditionsAsync()
{
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = false });
using var page = await browser.NewPageAsync();
// Emulate mobile device
await page.EmulateAsync(new Device
{
Name = "iPhone 12",
UserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1",
Viewport = new ViewPortOptions
{
Width = 390,
Height = 844,
DeviceScaleFactor = 3,
IsMobile = true,
HasTouch = true
}
});
// Apply mobile network conditions
await page.EmulateNetworkConditionsAsync(new NetworkConditions
{
Offline = false,
DownloadThroughput = 1.6 * 1024 * 1024 / 8, // Typical 3G speed
UploadThroughput = 750 * 1024 / 8,
Latency = 300 // Higher latency for mobile
});
await page.GoToAsync("https://example.com");
// Test mobile-specific interactions
await page.TapAsync("#mobile-menu-button");
await page.WaitForSelectorAsync(".mobile-menu");
}
Network Condition Testing with Custom Profiles
Creating Custom Network Profiles
Create profiles that match real-world conditions more accurately:
public static class CustomNetworkProfiles
{
// Based on real-world measurements
public static NetworkConditions SlowWiFi => new()
{
Offline = false,
DownloadThroughput = 2 * 1024 * 1024 / 8, // 2 Mbps
UploadThroughput = 1 * 1024 * 1024 / 8, // 1 Mbps
Latency = 100 // 100ms latency
};
public static NetworkConditions SatelliteInternet => new()
{
Offline = false,
DownloadThroughput = 10 * 1024 * 1024 / 8, // 10 Mbps
UploadThroughput = 1 * 1024 * 1024 / 8, // 1 Mbps
Latency = 600 // 600ms latency (high satellite delay)
};
public static NetworkConditions Edge => new()
{
Offline = false,
DownloadThroughput = 240 * 1024 / 8, // 240 Kbps
UploadThroughput = 200 * 1024 / 8, // 200 Kbps
Latency = 840 // 840ms latency
};
}
Testing Multiple Scenarios Sequentially
Run comprehensive network condition tests:
public async Task TestMultipleNetworkScenariosAsync()
{
var networkScenarios = new Dictionary<string, NetworkConditions>
{
{ "Offline", NetworkProfiles.Offline },
{ "EDGE (2G)", CustomNetworkProfiles.Edge },
{ "Slow 3G", NetworkProfiles.Slow3G },
{ "Fast 3G", NetworkProfiles.Fast3G },
{ "4G", NetworkProfiles.Regular4G },
{ "Slow WiFi", CustomNetworkProfiles.SlowWiFi },
{ "Satellite", CustomNetworkProfiles.SatelliteInternet }
};
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
using var page = await browser.NewPageAsync();
var results = new Dictionary<string, TimeSpan>();
foreach (var scenario in networkScenarios)
{
Console.WriteLine($"Testing {scenario.Key}...");
await page.EmulateNetworkConditionsAsync(scenario.Value);
if (scenario.Value.Offline)
{
// Test offline functionality
results[scenario.Key] = await TestOfflineScenario(page);
}
else
{
// Test online functionality with network conditions
results[scenario.Key] = await TestOnlineScenario(page);
}
}
// Print results
foreach (var result in results)
{
Console.WriteLine($"{result.Key}: {result.Value.TotalMilliseconds:F2}ms");
}
}
private async Task<TimeSpan> TestOnlineScenario(Page page)
{
var stopwatch = Stopwatch.StartNew();
try
{
await page.GoToAsync("https://httpbin.org/delay/1", new NavigationOptions
{
Timeout = 60000
});
await page.WaitForSelectorAsync("body");
}
catch (Exception ex)
{
Console.WriteLine($"Online test failed: {ex.Message}");
}
stopwatch.Stop();
return stopwatch.Elapsed;
}
private async Task<TimeSpan> TestOfflineScenario(Page page)
{
var stopwatch = Stopwatch.StartNew();
try
{
// Test offline functionality - should fail quickly
await page.GoToAsync("https://httpbin.org/delay/1", new NavigationOptions
{
Timeout = 5000
});
}
catch (Exception)
{
// Expected to fail in offline mode
}
stopwatch.Stop();
return stopwatch.Elapsed;
}
Handling Network Emulation Edge Cases
Resetting Network Conditions
Always reset network conditions when switching between tests:
public async Task ResetNetworkConditionsAsync(Page page)
{
// Reset to normal network conditions
await page.EmulateNetworkConditionsAsync(new NetworkConditions
{
Offline = false,
DownloadThroughput = 0, // 0 means no throttling
UploadThroughput = 0, // 0 means no throttling
Latency = 0 // 0 means no additional latency
});
}
Handling Timeout Issues
When testing under slow network conditions, you may need to adjust timeouts accordingly, similar to handling timeouts in Puppeteer:
public async Task HandleSlowNetworkTimeoutsAsync()
{
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = false });
using var page = await browser.NewPageAsync();
// Set longer default timeout for slow network conditions
page.DefaultTimeout = 60000; // 60 seconds
await page.EmulateNetworkConditionsAsync(NetworkProfiles.Slow3G);
try
{
await page.GoToAsync("https://example.com", new NavigationOptions
{
Timeout = 90000 // 90 seconds for navigation
});
await page.WaitForSelectorAsync(".main-content", new WaitForSelectorOptions
{
Timeout = 45000 // 45 seconds for content to appear
});
}
catch (TimeoutException ex)
{
Console.WriteLine($"Timeout occurred under slow network conditions: {ex.Message}");
}
}
Best Practices for Network Emulation Testing
1. Test Multiple Scenarios
Create comprehensive test suites that cover various network conditions:
[TestMethod]
public async Task TestApplicationUnderVariousNetworkConditions()
{
var testScenarios = new[]
{
("Offline", NetworkProfiles.Offline),
("Slow 3G", NetworkProfiles.Slow3G),
("Fast 3G", NetworkProfiles.Fast3G),
("4G", NetworkProfiles.Regular4G)
};
foreach (var (scenarioName, networkCondition) in testScenarios)
{
await RunNetworkScenarioTest(scenarioName, networkCondition);
}
}
private async Task RunNetworkScenarioTest(string scenarioName, NetworkConditions condition)
{
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
using var page = await browser.NewPageAsync();
await page.EmulateNetworkConditionsAsync(condition);
try
{
if (condition.Offline)
{
// Test offline behavior
await TestOfflineFunctionality(page);
}
else
{
// Test online functionality under specific network conditions
await TestOnlineFunctionality(page, scenarioName);
}
Console.WriteLine($"✓ {scenarioName} test passed");
}
catch (Exception ex)
{
Console.WriteLine($"✗ {scenarioName} test failed: {ex.Message}");
}
}
2. Measure and Log Performance Metrics
Track performance metrics across different network conditions:
public async Task MeasurePerformanceMetrics(Page page)
{
var performanceMetrics = await page.EvaluateExpressionAsync<dynamic>(@"
JSON.stringify({
loadTime: performance.timing.loadEventEnd - performance.timing.navigationStart,
domContentLoaded: performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart,
firstPaint: performance.getEntriesByType('paint')[0]?.startTime || 0,
resourceCount: performance.getEntriesByType('resource').length
})
");
var metrics = JsonConvert.DeserializeObject(performanceMetrics.ToString());
Console.WriteLine($"Performance Metrics: {metrics}");
}
3. Combine with Request Interception
For more advanced testing, combine network emulation with request interception:
public async Task TestWithRequestInterceptionAsync()
{
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = false });
using var page = await browser.NewPageAsync();
// Enable request interception
await page.SetRequestInterceptionAsync(true);
page.Request += async (sender, e) =>
{
// Simulate additional delays for specific resources
if (e.Request.Url.Contains("slow-resource"))
{
await Task.Delay(2000); // Additional 2-second delay
}
await e.Request.ContinueAsync();
};
// Apply network conditions
await page.EmulateNetworkConditionsAsync(NetworkProfiles.Slow3G);
await page.GoToAsync("https://example.com");
}
Network emulation with Puppeteer-Sharp is a powerful testing technique that helps ensure your web applications provide optimal user experiences across different connectivity scenarios. By simulating various network conditions, you can identify performance bottlenecks, test offline functionality, and optimize your application's behavior for users with limited bandwidth or intermittent connectivity.
Remember to combine network emulation with other Puppeteer-Sharp features like device emulation and performance monitoring to create comprehensive testing scenarios that reflect real-world usage patterns. This approach helps build more resilient and user-friendly web applications that work well regardless of network conditions.