如何在Xamarin Forms中使用NUnit对网络连接功能进行单元测试
Xamarin.Forms网络检测逻辑的NUnit单元测试实现
你当前的代码直接依赖Xamarin.Essentials的静态Connectivity类,静态依赖无法在单元测试中灵活控制返回值,没法隔离外部系统状态做独立测试,所以先做最小化的可测试性改造,再写用例即可。
前置改造:解耦静态依赖
不要在业务逻辑里直接调用静态Connectivity,先抽一层薄抽象把静态调用包装起来,既不影响生产逻辑,也能方便测试时模拟不同网络状态:
// 抽离网络状态访问的抽象 public interface IConnectivityService { NetworkAccess CurrentNetworkAccess { get; } } // 生产环境实现,直接包装Xamarin.Essentials的静态调用 public class XamarinEssentialsConnectivityService : IConnectivityService { public NetworkAccess CurrentNetworkAccess => Connectivity.NetworkAccess; }
再改造原有的InternetConnection类,通过构造函数注入抽象,同时把硬编码的2秒重检延迟做成可配置参数——测试时可以传极小的延迟值,避免每次测试等2秒拖慢执行速度:
public class InternetConnection : IInternetConnection { private readonly IConnectivityService _connectivityService; private readonly int _recheckDelayMilliseconds; public InternetConnection(IConnectivityService connectivityService, int recheckDelayMilliseconds = 2000) { _connectivityService = connectivityService; _recheckDelayMilliseconds = recheckDelayMilliseconds; } public async Task<bool> CheckInternetStatusAsync() { if (_connectivityService.CurrentNetworkAccess != NetworkAccess.Internet) { // 延迟重检处理公共场所网络瞬断 await Task.Delay(_recheckDelayMilliseconds); if (_connectivityService.CurrentNetworkAccess != NetworkAccess.Internet) { return false; } } return true; } }
生产环境使用时,注入XamarinEssentialsConnectivityService实例即可,原有业务逻辑完全不受影响。
NUnit测试用例编写
你可以用Moq这类Mock框架模拟IConnectivityService的返回值,覆盖全部3个核心逻辑分支:
- 首次检测即联网,直接返回true,不触发延迟等待
- 首次检测断网,等待重检时网络恢复(对应瞬断场景),返回true
- 两次检测都断网,返回false
测试代码如下:
using Moq; using NUnit.Framework; [TestFixture] public class InternetConnectionTests { [Test] public async Task CheckInternetStatusAsync_FirstCheckHasInternet_ReturnsTrueWithoutWait() { // 安排测试上下文 var mockConnectivity = new Mock<IConnectivityService>(); mockConnectivity.Setup(c => c.CurrentNetworkAccess).Returns(NetworkAccess.Internet); const int testDelay = 10; // 测试场景用极短延迟,避免拖慢测试 var testInstance = new InternetConnection(mockConnectivity.Object, testDelay); var startTime = DateTime.UtcNow; // 执行测试方法 var result = await testInstance.CheckInternetStatusAsync(); var timeCost = (DateTime.UtcNow - startTime).TotalMilliseconds; // 断言结果 Assert.IsTrue(result); Assert.Less(timeCost, testDelay); // 验证没有走到延迟重检分支 } [Test] public async Task CheckInternetStatusAsync_BlipRecovered_ReturnsTrue() { // 安排测试上下文:第一次调用返回断网,第二次返回联网,模拟瞬断恢复 var mockConnectivity = new Mock<IConnectivityService>(); var callCount = 0; mockConnectivity.Setup(c => c.CurrentNetworkAccess) .Returns(() => callCount++ == 0 ? NetworkAccess.None : NetworkAccess.Internet); const int testDelay = 10; var testInstance = new InternetConnection(mockConnectivity.Object, testDelay); // 执行测试方法 var result = await testInstance.CheckInternetStatusAsync(); // 断言结果 Assert.IsTrue(result); mockConnectivity.Verify(c => c.CurrentNetworkAccess, Times.Exactly(2)); // 验证确实触发了重检 } [Test] public async Task CheckInternetStatusAsync_NoNetworkAfterRecheck_ReturnsFalse() { // 安排测试上下文:始终返回断网 var mockConnectivity = new Mock<IConnectivityService>(); mockConnectivity.Setup(c => c.CurrentNetworkAccess).Returns(NetworkAccess.None); const int testDelay = 10; var testInstance = new InternetConnection(mockConnectivity.Object, testDelay); // 执行测试方法 var result = await testInstance.CheckInternetStatusAsync(); // 断言结果 Assert.IsFalse(result); mockConnectivity.Verify(c => c.CurrentNetworkAccess, Times.Exactly(2)); } }
补充说明
- 如果不想引入Moq依赖,也可以自己写个简单的测试桩类实现
IConnectivityService,手动控制CurrentNetworkAccess的返回值,测试效果完全一致 - 异步测试方法必须返回
Task,不要写async void,否则NUnit无法正确捕获测试执行过程中的异常 - 生产环境注入
InternetConnection时不用传延迟参数,默认就是你需要的2秒重检间隔
内容的提问来源于stack exchange,提问作者MainakChoudhury
相关产品推荐
相关产品推荐

