我有以下两个单元测试:一个使用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。
最佳答案
显然,由于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 Log Viewer(fuslogvw.exe)记录程序集绑定,您应该能够准确计算出正在加载的程序集以及在什么加载上下文中。
更新
我很确定这是由JSON转换器在运行时使用程序集引起的程序集绑定问题。在融合日志中,我找到了以下条目:
*** Assembly Binder日志条目(05/06/2015 @ 02:01:38)***
操作成功。
绑定结果:hr = 0x0。操作成功完成。
程序集管理器从以下位置加载:C:\ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ 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 \ bin \ Debug \ StackOverflow.30643046.dll。
日志:IJW程序集绑定返回了另一个路径:C:\ Users \ Tim \ AppData \ Local \ Temp \ k3dpwn5u.uii \ Machine Specification Runner \ assembly \ dl3 \ 6c41c492 \ c7eea8ec_279fd001 \ StackOverflow.30643046.dll。使用提供的文件。
我也发现了这个:
*** Assembly Binder日志条目(05/06/2015 @ 02:01:38)***
操作成功。
绑定结果:hr = 0x0。操作成功完成。
程序集管理器从以下位置加载:C:\ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ clr.dll
在可执行文件C:\ Users \ Tim \ AppData \ Local \ JetBrains \ Installations \ ReSharperPlatformVs12_001 \ JetBrains.ReSharper.TaskRunner.CLR45.x64.exe下运行
---详细的错误日志如下。
WRN:同一程序集已加载到应用程序域的多个上下文中:
WRN:上下文:默认|域ID:2 |程序集名称:StackOverflow.30643046,版本= 1.0.0.0,文化=中性,PublicKeyToken =空
WRN:上下文:都不域ID:2 |程序集名称:StackOverflow.30643046,版本= 1.0.0.0,文化=中性,PublicKeyToken =空
WRN:这可能会导致运行时失败。
WRN:建议检查您的应用程序是否故意。
WRN:有关此问题的更多信息和常见解决方案,请参见白皮书http://go.microsoft.com/fwlink/?LinkId=109270。
*** Assembly Binder日志条目(05/06/2015 @ 02:04:41)***
操作成功。
绑定结果:hr = 0x0。操作成功完成。
程序集管理器从以下位置加载:C:\ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ clr.dll
在可执行文件C:\ Users \ Tim \ AppData \ Local \ JetBrains \ Installations \ ReSharperPlatformVs12_001 \ JetBrains.ReSharper.TaskRunner.CLR45.x64.exe下运行
---详细的错误日志如下。
WRN:同一程序集已加载到应用程序域的多个上下文中:
WRN:上下文:默认|域ID:2 |程序集名称:StackOverflow.30643046,版本= 1.0.0.0,文化=中性,PublicKeyToken =空
WRN:上下文:都不域ID:2 |程序集名称:StackOverflow.30643046,版本= 1.0.0.0,文化=中性,PublicKeyToken =空
WRN:这可能会导致运行时失败。
WRN:建议检查您的应用程序是否故意。
WRN:有关此问题的更多信息和常见解决方案,请参见白皮书http://go.microsoft.com/fwlink/?LinkId=109270。
***装订夹日志输入(05/06/2015 @ 02:04:42)***
操作成功。
绑定结果:hr = 0x0。操作成功完成。
程序集管理器从以下位置加载:C:\ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319 \ clr.dll
在可执行文件C:\ Users \ Tim \ AppData \ Local \ JetBrains \ Installations \ ReSharperPlatformVs12_001 \ JetBrains.ReSharper.TaskRunner.CLR45.x64.exe下运行
---详细的错误日志如下。
WRN:同一程序集已加载到应用程序域的多个上下文中:
WRN:上下文:默认|域ID:2 |程序集名称:StackOverflow.30643046,版本= 1.0.0.0,文化=中性,PublicKeyToken =空
WRN:上下文:都不域ID:2 |程序集名称:StackOverflow.30643046,版本= 1.0.0.0,文化=中性,PublicKeyToken =空
WRN:这可能会导致运行时失败。
WRN:建议检查您的应用程序是否故意。
WRN:有关此问题的更多信息和常见解决方案,请参见白皮书http://go.microsoft.com/fwlink/?LinkId=109270。
我还发现,禁用ReSharper单元测试选项“正在测试的卷影副本程序集”会导致测试通过。
所以我认为我们有“吸烟枪”。由于让JSON反序列化器在运行时发现类型的方式,您的程序集加载有冲突。
更新2015-06-11
我在MSpec邮件列表中注意到this,它可能与您的问题有关:[machine.specifications] Shadow copying broken - creates very subtle bugs in tests