我正在try 对执行证书验证的服务进行单元测试.它将给定的 fingerprint 与存储在Azure密钥库中的证书上的 fingerprint 进行比较. 我的代码运行得很好,但现在我正在try (使用Moq)对其进行单元测试.

为了进行测试,我需要访问/moq检索到的证书的 fingerprint (证书是类型为KeyVaultCertificateWithPolicy的对象). 我的问题是,当我模拟证书的Properties对象时,Moq不能模拟X509Thumbprint,因为setter是内部的.

我收到以下代码的错误信息:

System.NotSupportdException:不支持的表达式:x=>x.X509Thumbprint 不能在设置/验证表达式中使用不可重写的成员(此处为:证书属性.Get_X509Thumbprint).

这是我的单元测试代码的摘录:

var azCertificateProps = new Mock<CertificateProperties>();
azCertificateProps.SetupGet(x => x.X509Thumbprint).Returns(testThumbprint); // Fails here
var azCertificate = new Mock<KeyVaultCertificateWithPolicy>(azCertificateProps.Object);

作为参考,这是我try 进行单元测试的实际代码(简化)

public KeyVaultCertificateWithPolicy ExpectedCertificate { get; set; }

public async Task<bool> ValidateCertificate(X509Certificate2 clientCertificate, CancellationToken cancellationToken)
{
    return clientCertificate.Thumbprint == BitConverter.ToString(ExpectedCertificate.Properties.X509Thumbprint).Replace("-", "");
}

我try 实例化KeyVaultCertificateWithPolicy对象本身,但仍然找不到以任何方式传递我的 fingerprint 的方法.

推荐答案

异常实际上不是由internal setter引起的.这是因为Moq的工作方式(以及大多数其他mocking框架)是通过override-ing方法或属性.在dotnet中,你只能覆盖virtual个东西.

这就是为什么通常情况下,Moq与Interages一起工作得很好,因为一切都是virtual.但是对于具体的类,您将开始遇到这些限制.

下面是一个例子:

class Test { public int Prop {get; set;} }
class TestVirt { public virtual int Prop {get; set;} }

new Mock<Test>().SetupGet(x => x.Prop).Returns(1); // FAILS!
new Mock<TestVirt>().SetupGet(x => x.Prop).Returns(1); // works

// Same problems with non-virtual methods.

对于类,我们通常只创建它的一个实例,而不是模仿.但我们已经知道它不会为你工作,因为internal二传手.

这就是我们拿出System.Reflection本神秘大部头的地方,使用类型系统给我们一个对象内部的后门.如果我们愿意,我们甚至可以设置private个变量,只要我们知道它的名字.

var testThumb = new byte[] { 1, 2, 3 };
var certProps = new CertificateProperties("");

typeof(CertificateProperties)
  .GetProperty("X509Thumbprint")? // Get property by name,
  .SetValue(certProps, testThumb); // and set its value.

Debug.Assert(certProps.X509Thumbprint.SequenceEqual(testThumb));

反射的一个缺点是,我们会让类成员使用其名称的string.如果类提供程序决定更改其签名并移除该属性,则只会在运行时而不是编译时发现.

Csharp相关问答推荐

Microsoft.AspNetCore.Mvc. Controller Base.用户:属性或索引器Controller Base.用户无法分配给--它是只读的

Blazor:计算值或保留为默认值

.NET最小API映射将T参数列表为[FromQuery]

. NET 8 HttpClient post参数将其情况更改为camel'

我无法在Ubuntu下编译使用microsoft.extension.configurationbuilder jsonapi和mono mcs的c#应用程序

System. InvalidOperationException:无法将数据库中的字符串值i转换为映射的ItemType枚举中的任何值''''

如何在Reflection. Emit中使用具有运行时定义的类型参数的泛型类型

Elasticsearch:当我try 使用c#将嵌套对象添加到filter中时出现问题

. NET 8控制台应用程序DI错误无法解析Microsoft. Extension. Logging. ILoggerFactory类型的服务'''

具有单一导航属性的EF核心一对多关系

如何在WPF的树视图中显示一个对象的两个或多个属性,其中只有一个是分层项?

为什么无法将对象转换为泛型类型

HttpClient 415不支持的媒体类型错误

如何避免在.NET中将日志(log)写入相对路径

EF Core 7-忽略模型绑定中的虚拟属性

在构造函数中传递C#函数以用作EventHandler委托的订阅服务器

Visual Studio如何使用当前的框架?

并发表更新.EF核心交易

使用ImmutableList时,DynamicData未按预期工作

从列表中跳过和获取条目的优雅方式