[英]Unit Testing AppContext Switches
更改日志
我有一段代码,我正在为它编写单元测试来标记一些 xml。
我在开发代码时注意到 .NET 4.7.1+ 会抛出CryptographicException: Invalid algorithm specified
。 为了解决这个问题,我使用了以下AppContext
开关(来源: wiktor zychla ,为什么我指定的算法无效... )。
我已将以下代码放在任何调用我的 xml 签名代码的应用程序中:
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
在野外运行时,此代码运行正常,并且我的 xml 正在按预期签名。
我已经为这段代码编写了一些单元测试,这是我第一次真正尝试编写单元测试,我遇到了一些奇怪的边缘情况。
try
{
// Compute the signature.
signedXml.ComputeSignature();
}
catch (CryptographicException cex)
{
//Contains instead of == since this message contains newline characters...
if (cex.Message.Contains("Invalid algorithm specified"))
{
string code = Environment.NewLine + "AppContext.SetSwitch(\"Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms\", true);" +
Environment.NewLine + "AppContext.SetSwitch(\"Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms\", true); ";
throw new Exception($"Cryptographic exception was thrown, ensure that the following switches are set in the executing assembly: {code}", cex);
}
else
{
throw cex;
}
}
所以如果抛出异常,我会显示一条很好的消息,说请在调用此方法之前添加以下代码。
我的测试类看起来像这样:
[TestFixture]
public class XMLSigningTests
{
protected IXMLSigner _xmlSigner;
protected byte[] _publicKey;
protected string _password;
[SetUp]
public void Init()
{
_xmlSigner = new XMLSigner();
_password = "test";
_publicKey = GenerateTestCert(_password);
}
[TearDown]
public void CleanUp()
{
_xmlSigner = null;
_publicKey = null;
_password = null;
}
[Test]
public void SignXML_AppContextSwitchNotSet_Fail()
{
XMLDocument xmlDoc = GenerateXML();
Assert.Throws<Exception>(() => { _xmlSigner.SignXML(xmlDoc, _publicKey, _password); });
}
[Test]
public void SignXML_AppContextSwitchSet_Success()
{
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
XMLDocument xmlDoc = GenerateXML();
var signedXml = _xmlSigner.SignXML(xmlDoc, _publicKey, _password);
Assert.IsFalse(signedXML == null);
//More validation here
}
private XMLDocument GenerateXML()
{
return new XMLDocument("Some fancy xml here...");
}
private byte[] GenerateTestCert(string password)
{
//Use Pluralsight.Crypto to generate a self signed cert & export as .pfx
return cert.Export(X509ContentType.Pfx, password);
}
}
当我运行我希望自己通过的SignXML_AppContextSwitchSet_Success
,测试每次都通过(只是我反复运行它)。 如果我清理我的解决方案并运行所有测试,测试总是失败。
我的 xml 生成类没有共享对象,它实际上可能是静态的。
测试失败,因为加密异常被抛出(触发我的代码并抛出异常告诉开发人员(我)添加应用程序上下文切换)。
有没有办法验证配置开关是否受到尊重?
从我的控制台测试器应用程序和我内置的 wpf 应用程序运行代码时,永远不会抛出异常。 只有当我背靠背运行所有单元测试时才会抛出它。 我希望这些测试在 azure 管道上运行,因此它可以同时运行以及单独运行非常重要。
我对这个例外的想法是,也许当我一起运行所有测试时, AppContext
开关没有得到尊重。 我尝试在签署 xml 之前添加检查:
AppContext.TryGetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", out bool xmlSwitch);
AppContext.TryGetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", out bool pkcsSwitch);
if (!xmlSwitch || !pkcsSwitch)
{
throw new NotImplementedException("THESE VALUES SHOULD BE SET WTF");
}
现在,您希望此代码无法通过我的失败测试(未设置开关),并且确实如此,您可以在测试资源管理器中看到我粗略的NotImplementedException
消息。 然而,我期望成功的测试再次抛出CryptographicException
......
System.Exception : Cryptographic exception was thrown, ensure that the following switches are set in the executing assembly:
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
----> System.Security.Cryptography.CryptographicException : Invalid algorithm specified.
正如我之前所说,我对测试很陌生,我不确定我的测试设计方法是否错误,如果我只是在测试一些底层代码,所以也许我注定要尝试测试它或我缺少某些东西以允许正确测试此案例。
我目前正在使用NUnit 3.12.0 + NUnit3TestAdapter 3.15.1
来编写测试,尽管我最初使用的是MSTest 1.3.2
并且在使用这两个框架时遇到了相同的行为。
我发现以下测试的执行顺序决定了成功:
我通过阅读诸如( 单元测试在运行所有时失败,但在单独运行时通过)之类的帖子中发现了这一点,这些帖子在未单独运行时失败的测试中。
它让我考虑设置的值,我想要以下情况:
//Test I expect to fail
AppContext.SetSwitch("the switch..", false);
//Test I expect to pass
AppContext.SetSwitch("the switch..", true);
尽管我已经测试过这些值的设置是否正确,但底层开关是否正在更新? 如何清理这些底层对象?
我在NUnit 的 GitHub (appdomain per test (isolation per test), for processing static classes)上发现了以下问题。 在这个线程中,他们讨论了每个测试的新AppDomain
选项,这正是我所需要的。
jnm2发布了一个 util 类来在单独的AppDomain
( code ) 中运行代码(我不会在这里发布它,因为这篇文章已经很大了)。 我使用了这个 util 类并调整了我的测试以使用它(您不能使用任何类变量或方法,这意味着我必须复制我的辅助方法代码)。
一旦我实现了 util 并调整了我的测试以符合它的标准,当我单独运行它们时,测试开始通过,并且一次全部通过。
任务完成!
我遇到了完全相同的问题! 我遇到了这篇文章,它激发了我的解决方案!
我没有在单独的 AppDomain 中运行测试,而是修改了调用加密库的生产代码。 我在一个单独的 AppDomain 中运行有问题的调用,我在继续之前设置了开关。
这修复了所有测试以及它使我的库的客户端不会弄乱开关。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.