我有以下两个单元测试:一个使用MSTest,另一个使用Machine Specification。在我看来,他们的行为应该是一样的。然而,虽然第一个在NCrunch和ReSharper测试中都通过了,但第二个在ReSharper中失败了。
using Machine.Specifications;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
public class TestModel
{
public string Name { get; set; }
public int Number { get; set; }
}
// MSTest
[TestClass]
public class DeserializationTest
{
[TestMethod]
public void Deserialized_object_is_the_same_type_as_the_original()
{
TestModel testModel = new TestModel() {Name = "John", Number = 42};
string serialized = JsonConvert.SerializeObject(testModel, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects });
object deserialized = JsonConvert.DeserializeObject(serialized, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects });
// This passes in both test runners
Assert.IsInstanceOfType(deserialized, typeof(TestModel));
}
}
// MSpec
public class When_an_object_is_deserialized
{
static TestModel testModel;
static string serialized;
static object deserialized;
Establish context = () =>
{
testModel = new TestModel() { Name = "John", Number = 42 };
serialized = JsonConvert.SerializeObject(testModel, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects });
};
Because of = () => deserialized = JsonConvert.DeserializeObject(serialized, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects });
// This passes in NCrunch but fails in ReSharper.
It should_be_the_same_type_as_the_original = () => Assert.IsInstanceOfType(deserialized, typeof(TestModel));
}
故障消息为:Assert.IsInstanceOfType failed. Expected type:<UnitTestProject2.TestModel>. Actual type:<UnitTestProject2.TestModel>.
奇怪的是,以下内容确实通过了:
It should_be_the_same_type_as_the_original = () => Assert.IsTrue(testModel.GetType() == typeof(TestModel));
我以这种方式进行反序列化,因为实际的代码需要能够处理在运行时之前类型未知的对象。我想Json的方式有些奇怪。NET进行这种反序列化,但为什么两个测试运行程序的行为不同呢?我在Visual Studio 2013中使用ReSharper 9.1。
很明显,由于NCrunch和ReSharper之间的一些细微行为差异,运行时会产生奇怪的影响。失败无疑是在告诉你出了问题,你不应该把它当作ReSharper或NCrunch中的一个bug来看待。
当我在调试器中逐步完成MSpec测试时,deserialized
对象在调试器中显示以下错误:
deserialized Cannot fetch the value of field 'deserialized' because information about the containing class is unavailable. object
如果没有看到完整的解决方案,很难确定,但我见过当生成输出目录包含多个程序集副本时会发生这种情况,可能在子目录中。如果不同的零部件在不同的时间引用某个部件的不同副本,则即使该部件实际上是该部件的相同副本,有时也会认为该部件的类型不相等。解决方案是确保在生成输出中每个程序集只有一个副本,这样可以确保所有程序集都引用完全相同的文件。这可能是因为JSON转换器正在动态加载您的类型并获得错误的程序集,或者可能将其加载到不同的加载上下文中,这意味着它的类型与在不同上下文中加载的副本不相等。
在MSpec的情况下,您的生成环境可能会导致程序集的重复副本。特别是NCruch,默认情况下不执行构建后事件(通常会显示相应的警告),因此,如果您在构建后步骤中复制文件,那么这可能是不同行为的一种解释。您可以通过在NCrunch中启用构建后事件并查看故障是否发生来检查这一点。
另一个可能的故障排除步骤是使用Fusion日志查看器(fuslogvw.exe)记录程序集绑定,您应该能够准确地确定哪些程序集正在加载以及在什么加载上下文中加载。
更新我确信这是由于JSON转换器在运行时使用程序集而导致的程序集绑定问题。在融合日志中,我发现了以下条目:
***程序集绑定日志条目(05/06/2015@02:01:38)***手术成功。绑定结果:hr=0x0。操作已成功完成。从C:\Windows\Microsoft加载程序集管理器。NET\Framework64\v4.0.30119\clr.dll在可执行文件C:\Users\Tim\AppData\Local\JetBrains\Installations\ReSharperPlatformVs12_001\JetBrains下运行。ReSharper。TaskRunner。CLR45.x64.exe---下面是详细的错误日志。日志:IJW显式绑定。文件路径:c:\users\tim\VS Projects\StackOverflow \StackOverflow.30643046.dll。日志:IJW程序集绑定返回了不同的路径:C:\Users\Tim\AppData\Local\Temp\k3dpwn5u.uii\Machine Specifications Runner\assembly\dl3\6c41c492\c7eea8ec_279fd001\StackOverflow.30643046.dll。请使用提供的文件
我还发现了这个:
***程序集绑定日志条目(05/06/2015@02:01:38)***手术成功。绑定结果:hr=0x0。操作已成功完成。从C:\Windows\Microsoft加载程序集管理器。NET\Framework64\v4.0.30119\clr.dll在可执行文件C:\Users\Tim\AppData\Local\JetBrains\Installations\ReSharperPlatformVs12_001\JetBrains下运行。ReSharper。TaskRunner。CLR45.x64.exe---下面是详细的错误日志。警告:同一程序集已加载到应用程序域的多个上下文中:WRN:上下文:默认|域ID:2 |程序集名称:StackOverflow.30643046,版本=1.0.0.0,区域性=中性,PublicKeyToken=nullWRN:上下文:两者都没有|域ID:2 |程序集名称:StackOverflow.30643046,版本=1.0.0.0,区域性=中性,PublicKeyToken=null警告:这可能会导致运行时故障。WRN:建议检查您的申请是否有意。WRN:请参阅白皮书http://go.microsoft.com/fwlink/?LinkId=109270了解有关此问题的更多信息和常见解决方案。***汇编装订日志条目(2015年6月5日02:04:41)***手术成功。绑定结果:hr=0x0。操作已成功完成。从以下位置加载程序集管理器:C:\Windows\Microsoft.NET\Framework64\v4.0.30119\clr.dll在可执行文件C:\Users\Tim\AppData\Local\JetBrains\Installations\ReSharperPlatformVs12_001\JetBrains.ReSharper.TaskRunner.CLR45.x64.exe下运行---下面是详细的错误日志。警告:同一程序集已加载到应用程序域的多个上下文中:WRN:上下文:默认|域ID:2 |程序集名称:StackOverflow.30643046,版本=1.0.0.0,区域性=中性,PublicKeyToken=nullWRN:上下文:两者都没有|域ID:2 |程序集名称:StackOverflow.30643046,版本=1.0.0.0,区域性=中性,PublicKeyToken=null警告:这可能会导致运行时故障。WRN:建议检查您的申请是否有意。WRN:请参阅白皮书http://go.microsoft.com/fwlink/?LinkId=109270了解有关此问题的更多信息和常见解决方案。***程序集装订日志条目(2015年6月5日02时04分42秒)***手术成功。绑定结果:hr=0x0。操作已成功完成。从以下位置加载程序集管理器:C:\Windows\Microsoft.NET\Framework64\v4.0.30119\clr.dll在可执行文件C:\Users\Tim\AppData\Local\JetBrains\Installations\ReSharperPlatformVs12_001\JetBrains.ReSharper.TaskRunner.CLR45.x64.exe下运行---下面是详细的错误日志。警告:同一程序集已加载到应用程序域的多个上下文中:WRN:上下文:默认|域ID:2 |程序集名称:StackOverflow.30643046,版本=1.0.0.0,区域性=中性,PublicKeyToken=nullWRN:上下文:两者都没有|域ID:2 |程序集名称:StackOverflow.30643046,版本=1.0.0.0,区域性=中性,PublicKeyToken=null警告:这可能会导致运行时故障。WRN:建议检查您的申请是否有意。WRN:请参阅白皮书http://go.microsoft.com/fwlink/?LinkId=109270了解有关此问题的更多信息和常见解决方案
我还发现,禁用ReSharper单元测试选项"正在测试的卷影复制程序集"会导致测试通过。
所以我认为我们有"确凿的证据"。由于您让JSON反序列化程序在运行时发现类型的方式,您有冲突的程序集加载。
更新2015-06-11
我在MSpec邮件列表中注意到了这一点,它可能与您的问题有关:[machine.specificates]卷影复制中断-在测试中产生非常微妙的错误
正如@tim long所提到的,这个问题(以及MSpec#278)的原因是MSpec运行程序中的一个错误。该错误是由ReSharper设置来卷影复制测试程序集(默认情况下为"on")触发的。
Json.NET的TypeNameHandling.Objects
选项导致程序集按程序集名称加载,因此使用卷影复制的测试程序集加载TestModel
类型,这与MSpec运行程序用于加载和运行测试程序集的非卷影复制版本不同。这会导致">预期类型:<UnitTestProject2.TestModel>.实际类型:<UnitTestProject2.TestModel>"失败。有关启用卷影复制时测试程序集加载两次的详细信息,请参阅#279。
我能够重现这个故障,并检查我的修复#279也解决了这个问题。