我有以下两个单元测试:一个使用MSTest,另一个使用机器规格。据我所知,他们应该表现得相同。然而,当第一个通过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。
答案 0 :(得分:3)
显然,由于NCrunch和ReSharper之间的行为存在一些微妙的差异,因此会出现奇怪的运行时效果。失败肯定会告诉你一些错误,你不应该把它当作ReSharper或NCrunch中的错误。
当我在调试器中单步执行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)来记录程序集绑定,您应该能够准确计算出正在加载的程序集以及负载上下文。
UPDATE 我很确定这是由JSON转换器在运行时使用程序集引起的程序集绑定问题。在融合日志中,我找到了这个条目:
*** Assembly Binder Log Entry (05/06/2015 @ 02:01:38) *** The operation was successful. Bind result: hr = 0x0. The operation completed successfully. Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll Running under executable C:\Users\Tim\AppData\Local\JetBrains\Installations\ReSharperPlatformVs12_001\JetBrains.ReSharper.TaskRunner.CLR45.x64.exe --- A detailed error log follows. LOG: IJW explicit bind. File path:c:\users\tim\VS-Projects\StackOverflow\StackOverflow.30643046\bin\Debug\StackOverflow.30643046.dll. LOG: IJW assembly bind returned a different path: C:\Users\Tim\AppData\Local\Temp\k3dpwn5u.uii\Machine Specifications Runner\assembly\dl3\6c41c492\c7eea8ec_279fd001\StackOverflow.30643046.dll. Use the file provided.
我也找到了这个:
*** Assembly Binder Log Entry (05/06/2015 @ 02:01:38) *** The operation was successful. Bind result: hr = 0x0. The operation completed successfully. Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll Running under executable C:\Users\Tim\AppData\Local\JetBrains\Installations\ReSharperPlatformVs12_001\JetBrains.ReSharper.TaskRunner.CLR45.x64.exe --- A detailed error log follows. WRN: The same assembly was loaded into multiple contexts of an application domain: WRN: Context: Default | Domain ID: 2 | Assembly Name: StackOverflow.30643046, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null WRN: Context: Neither | Domain ID: 2 | Assembly Name: StackOverflow.30643046, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null WRN: This might lead to runtime failures. WRN: It is recommended to inspect your application on whether this is intentional or not. WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue. *** Assembly Binder Log Entry (05/06/2015 @ 02:04:41) *** The operation was successful. Bind result: hr = 0x0. The operation completed successfully. Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll Running under executable C:\Users\Tim\AppData\Local\JetBrains\Installations\ReSharperPlatformVs12_001\JetBrains.ReSharper.TaskRunner.CLR45.x64.exe --- A detailed error log follows. WRN: The same assembly was loaded into multiple contexts of an application domain: WRN: Context: Default | Domain ID: 2 | Assembly Name: StackOverflow.30643046, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null WRN: Context: Neither | Domain ID: 2 | Assembly Name: StackOverflow.30643046, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null WRN: This might lead to runtime failures. WRN: It is recommended to inspect your application on whether this is intentional or not. WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue. *** Assembly Binder Log Entry (05/06/2015 @ 02:04:42) *** The operation was successful. Bind result: hr = 0x0. The operation completed successfully. Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll Running under executable C:\Users\Tim\AppData\Local\JetBrains\Installations\ReSharperPlatformVs12_001\JetBrains.ReSharper.TaskRunner.CLR45.x64.exe --- A detailed error log follows. WRN: The same assembly was loaded into multiple contexts of an application domain: WRN: Context: Default | Domain ID: 2 | Assembly Name: StackOverflow.30643046, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null WRN: Context: Neither | Domain ID: 2 | Assembly Name: StackOverflow.30643046, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null WRN: This might lead to runtime failures. WRN: It is recommended to inspect your application on whether this is intentional or not. WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
我还发现禁用ReSharper单元测试选项“正在测试的卷影复制程序集”会导致测试通过。
所以我认为我们有'吸烟枪'。由于JSON反序列化程序在运行时发现类型的方式,您的程序集加载会发生冲突。
我在MSpec邮件列表中注意到this,它可能与您的问题有关:[machine.specifications] Shadow copying broken - creates very subtle bugs in tests
答案 1 :(得分:3)
正如@ tim-long提到的,这个问题的原因(和MSpec #278)是MSpec运行器中的一个错误。该错误由ReSharper设置触发阴影复制测试程序集(默认情况下为“on”)。
Json.NET的TypeNameHandling.Objects
选项导致程序集加载按程序集名称,因此使用卷影复制的测试程序集加载TestModel
类型,这种类型不同对于非阴影复制版本,MSpec运行器用于加载和运行测试程序集。这导致“预期类型:&lt; UnitTestProject2.TestModel&gt;。实际类型:&lt; UnitTestProject2.TestModel&gt; ”失败。有关在启用了卷影复制的情况下加载测试程序集两次的详细信息,请参阅#279。
我能够重现此失败并检查我的修复#279是否也解决了这个问题。