You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单元测试中如何Mock SslStream?Mock AuthenticateAsClientAsync方法及安全属性并覆盖异常分支

用Moq测试SslStream认证方法的全分支覆盖与异常触发

你已经选对了方向——因为SslStream的AuthenticateAsClientAsync和相关安全属性都是virtual/override,所以完全可以用Moq来模拟这些行为,进而覆盖AuthenticateSSLStream方法的所有条件分支。下面我会一步步拆解每个分支的测试方案,以及如何触发认证异常。

测试前置准备

首先,我们需要准备测试依赖:

  • 一个测试用的X509Certificate2(可以用自签证书,测试场景下真实证书比Mock更稳妥)
  • Moq创建的SslStream实例
  • 测试框架(这里用xUnit举例,NUnit逻辑完全一致)

假设你的类定义如下:

public class SslAuthenticator
{
    private readonly X509Certificate2 myCertificate;

    public SslAuthenticator(X509Certificate2 certificate)
    {
        myCertificate = certificate;
    }

    internal bool AuthenticateSSLStream(SslStream sslStream)
    {
        string certName = myCertificate.GetNameInfo(X509NameType.SimpleName, false);
        bool isAuthenticationCompleted = sslStream.AuthenticateAsClientAsync(certName, new X509Certificate2Collection(myCertificate), SslProtocols.Tls11 | SslProtocols.Tls12, true).Wait(10000);
        if (!isAuthenticationCompleted || !sslStream.IsAuthenticated || !sslStream.IsEncrypted || !sslStream.IsSigned)
        {
            throw new AuthenticationException("Authentication is not succeeded!");
        }
        return isAuthenticationCompleted;
    }
}

1. 覆盖所有条件分支

我们需要针对每个失败条件和成功条件编写单独的测试用例:

分支1:认证成功(所有条件满足)

这是方法的正常执行路径,需要模拟异步认证完成,且三个安全属性均为true:

[Fact]
public void AuthenticateSSLStream_WhenAllConditionsMet_ReturnsTrue()
{
    // Arrange
    var testCert = new X509Certificate2("test-cert.pfx", "test-password"); // 替换为你的测试证书
    var authenticator = new SslAuthenticator(testCert);
    var mockSslStream = new Mock<SslStream>();

    // 模拟异步认证立即完成
    mockSslStream.Setup(s => s.AuthenticateAsClientAsync(
        It.IsAny<string>(),
        It.IsAny<X509Certificate2Collection>(),
        It.IsAny<SslProtocols>(),
        It.IsAny<bool>()))
        .Returns(Task.CompletedTask);

    // 设置安全属性为true
    mockSslStream.SetupGet(s => s.IsAuthenticated).Returns(true);
    mockSslStream.SetupGet(s => s.IsEncrypted).Returns(true);
    mockSslStream.SetupGet(s => s.IsSigned).Returns(true);

    // Act
    var result = authenticator.AuthenticateSSLStream(mockSslStream.Object);

    // Assert
    Assert.True(result);
}

分支2:认证超时(Wait返回false)

模拟异步认证未在10秒内完成,触发异常:

[Fact]
public void AuthenticateSSLStream_WhenAuthenticationTimesOut_ThrowsAuthenticationException()
{
    // Arrange
    var testCert = new X509Certificate2("test-cert.pfx", "test-password");
    var authenticator = new SslAuthenticator(testCert);
    var mockSslStream = new Mock<SslStream>();

    // 模拟一个永远不会完成的Task,让Wait返回false
    var neverCompletedTask = new TaskCompletionSource<bool>().Task;
    mockSslStream.Setup(s => s.AuthenticateAsClientAsync(
        It.IsAny<string>(),
        It.IsAny<X509Certificate2Collection>(),
        It.IsAny<SslProtocols>(),
        It.IsAny<bool>()))
        .Returns(neverCompletedTask);

    // Act & Assert
    Assert.Throws<AuthenticationException>(() => authenticator.AuthenticateSSLStream(mockSslStream.Object));
}

分支3:认证完成但IsAuthenticated为false

模拟认证完成,但身份验证状态不满足:

[Fact]
public void AuthenticateSSLStream_WhenIsAuthenticatedFalse_ThrowsAuthenticationException()
{
    // Arrange
    var testCert = new X509Certificate2("test-cert.pfx", "test-password");
    var authenticator = new SslAuthenticator(testCert);
    var mockSslStream = new Mock<SslStream>();

    mockSslStream.Setup(s => s.AuthenticateAsClientAsync(It.IsAny<string>(), It.IsAny<X509Certificate2Collection>(), It.IsAny<SslProtocols>(), It.IsAny<bool>()))
        .Returns(Task.CompletedTask);

    mockSslStream.SetupGet(s => s.IsAuthenticated).Returns(false);
    mockSslStream.SetupGet(s => s.IsEncrypted).Returns(true);
    mockSslStream.SetupGet(s => s.IsSigned).Returns(true);

    // Act & Assert
    Assert.Throws<AuthenticationException>(() => authenticator.AuthenticateSSLStream(mockSslStream.Object));
}

分支4:认证完成但IsEncrypted为false

仅修改加密状态为false,触发异常:

[Fact]
public void AuthenticateSSLStream_WhenIsEncryptedFalse_ThrowsAuthenticationException()
{
    // Arrange
    var testCert = new X509Certificate2("test-cert.pfx", "test-password");
    var authenticator = new SslAuthenticator(testCert);
    var mockSslStream = new Mock<SslStream>();

    mockSslStream.Setup(s => s.AuthenticateAsClientAsync(It.IsAny<string>(), It.IsAny<X509Certificate2Collection>(), It.IsAny<SslProtocols>(), It.IsAny<bool>()))
        .Returns(Task.CompletedTask);

    mockSslStream.SetupGet(s => s.IsAuthenticated).Returns(true);
    mockSslStream.SetupGet(s => s.IsEncrypted).Returns(false);
    mockSslStream.SetupGet(s => s.IsSigned).Returns(true);

    // Act & Assert
    Assert.Throws<AuthenticationException>(() => authenticator.AuthenticateSSLStream(mockSslStream.Object));
}

分支5:认证完成但IsSigned为false

仅修改签名状态为false,触发异常:

[Fact]
public void AuthenticateSSLStream_WhenIsSignedFalse_ThrowsAuthenticationException()
{
    // Arrange
    var testCert = new X509Certificate2("test-cert.pfx", "test-password");
    var authenticator = new SslAuthenticator(testCert);
    var mockSslStream = new Mock<SslStream>();

    mockSslStream.Setup(s => s.AuthenticateAsClientAsync(It.IsAny<string>(), It.IsAny<X509Certificate2Collection>(), It.IsAny<SslProtocols>(), It.IsAny<bool>()))
        .Returns(Task.CompletedTask);

    mockSslStream.SetupGet(s => s.IsAuthenticated).Returns(true);
    mockSslStream.SetupGet(s => s.IsEncrypted).Returns(true);
    mockSslStream.SetupGet(s => s.IsSigned).Returns(false);

    // Act & Assert
    Assert.Throws<AuthenticationException>(() => authenticator.AuthenticateSSLStream(mockSslStream.Object));
}

2. 触发认证异常的其他方式

除了上述条件分支,你还可以模拟AuthenticateAsClientAsync本身抛出异常(比如证书无效、协议不支持等场景):

[Fact]
public void AuthenticateSSLStream_WhenAuthenticateThrowsException_ThrowsAuthenticationException()
{
    // Arrange
    var testCert = new X509Certificate2("test-cert.pfx", "test-password");
    var authenticator = new SslAuthenticator(testCert);
    var mockSslStream = new Mock<SslStream>();

    // 模拟认证方法直接抛出异常
    mockSslStream.Setup(s => s.AuthenticateAsClientAsync(It.IsAny<string>(), It.IsAny<X509Certificate2Collection>(), It.IsAny<SslProtocols>(), It.IsAny<bool>()))
        .ThrowsAsync(new InvalidOperationException("Invalid certificate provided"));

    // Act & Assert
    Assert.Throws<AuthenticationException>(() => authenticator.AuthenticateSSLStream(mockSslStream.Object));
}

这里用ThrowsAsync模拟异步方法抛出异常,Wait调用会捕获到这个异常,进而进入你的条件判断逻辑,最终抛出AuthenticationException。

内容的提问来源于stack exchange,提问作者Péter Szilvási

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 11:22:28