我正在尝试对进行证书验证的服务进行单元测试。它将给定指纹与存储在 Azure Key Vault 中的证书上的指纹进行比较。 我的代码工作正常,但现在我正在尝试对其进行单元测试(使用最小起订量)。
为了进行测试,我需要访问/最小起订量检索到的证书的指纹(该证书是类型为
KeyVaultCertificateWithPolicy
的对象。
我的问题是,当我模拟证书的 Properties
对象时,Moq 无法模拟 X509Thumbprint
,因为设置器是内部的。
我使用以下代码收到以下错误:
System.NotSupportedException:不支持的表达式:x => x.X509Thumbprint 不可重写的成员(此处:CertificateProperties.get_X509Thumbprint)不得在设置/验证表达式中使用。
这是我的单元测试代码的摘录:
var azCertificateProps = new Mock<CertificateProperties>();
azCertificateProps.SetupGet(x => x.X509Thumbprint).Returns(testThumbprint); // Fails here
var azCertificate = new Mock<KeyVaultCertificateWithPolicy>(azCertificateProps.Object);
作为参考,这是我正在尝试进行单元测试的实际代码(简化)
public KeyVaultCertificateWithPolicy ExpectedCertificate { get; set; }
public async Task<bool> ValidateCertificate(X509Certificate2 clientCertificate, CancellationToken cancellationToken)
{
return clientCertificate.Thumbprint == BitConverter.ToString(ExpectedCertificate.Properties.X509Thumbprint).Replace("-", "");
}
我尝试实例化
KeyVaultCertificateWithPolicy
对象本身,但我仍然找不到以任何方式传递我的指纹的方法。
异常实际上不是由
internal
setter 引起的。这是因为 Moq 的工作方式(以及大多数其他模拟框架)是通过 override
-ing 方法或属性。在 dotnet 中,您只能覆盖 virtual
事物。
这就是为什么 Moq 通常与接口配合得很好,因为一切都是
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
setter。
这就是我们拿出
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
来获取类成员。如果类提供者决定更改其签名并删除属性,您只会在运行时而不是编译时发现。