使用siteoforigin pack Uri对WPF应用程序进行单元测试

时间:2012-03-01 20:10:32

标签: wpf unit-testing mstest vs-unit-testing-framework

我正在为WPF应用程序编写一些单元测试,尽管我试图避免使用它,但我还是有一些正在测试的代码实例化View。一旦实例化视图,就会评估所有标记扩展,样式等。为了解决这个问题,我在初始化测试程序集时创建了一个虚拟的 Application 并注册了所有必需的资源:

[TestClass]
public class AssemblyInitialize
{
    [AssemblyInitialize]
    public static void SetupTestAssembly(TestContext context)
    {
        if (Application.Current == null)
            new Application();

        var resources = new List<string>
            {
              "pack://application:,,,/AssemblyName;component/ResourceDictionary.xaml"
            };

       foreach(var resource in resources)
       {
           var uri = new Uri(resource);
           var dictionary = new ResourceDictionary { Source = uri };
           Application.Current.Resources.MergedDictionaries.Add(dictionary);
       }
    }
}

我过去曾使用过这种方法,而且运行正常。

我用这种方法遇到了一个小麻烦。我有一些资源在包Uri中使用 pack:// siteoforigin:,当测试实例化此视图时,我收到一条关于无法解析文件的错误。

XAML:

<ResourceDictionary
   xmlns="...">

   <ImageBrush 
      x:Key="ResourceName"
      ImageSource="pack://siteoforigin:,,,/Resources/image.png" 
      />
</ResourceDictionary>

错误消息:

 Could not find a part of the path 'C:\\Solution\\TestResults\\Workspace_2012-03-01 14_54_29\\Resources\\image.png'

我已将Resources目录添加为部署项目,并且我已确认该图像是TestRun输出目录。似乎AppDomain在我的测试程序集的位置上方运行一个文件夹,因为该文件实际位于:

c:\ Solution \ TestResults \ Workspace_2012-03-01 14_54_29 \ Out \ Resources \ image.png

关于如何让WPF应用程序使用Out目录作为主文件夹的任何建议?

2 个答案:

答案 0 :(得分:3)

这是因为您的测试运行器设置的AppDomain.BaseDirectory没有尾随'/'字符,这会导致解析siteoforigin路径的代码丢失最后一个目录路径。

您可以通过在正常运行或测试中查看以下代码的结果来检查这一点。

Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory);

最近在NUnit中已经fixed(包含在2.6中),但可能仍然是其他测试跑步者的问题。

如果您感兴趣,这相当于siteoforigin代码正在执行的操作:

new Uri(new Uri(baseDirectory), "some/relative/path.jpg").LocalPath

baseDirectory中使用和不使用尾部斜杠尝试。

答案 1 :(得分:1)

我已经成功解决了这个问题,如answer above所述,通过反射更改了AppDomain.CurrentDomain.BaseDirectory

private static void FixWpfPackSiteOfOriginReferences()
{
    // note: xunit does not add a trailing slash to the BaseDirectory path.
    // When you start the application, there's a trailing slash.
    // Also see:
    // - https://stackoverflow.com/a/9908449/684096
    // - https://github.com/dotnet/runtime/issues/7866#issuecomment-299761968

    var appDomainSetup = GetInstancePropertyValueViaReflection<AppDomainSetup>(
        AppDomain.CurrentDomain,
        "FusionStore");

    if (!appDomainSetup.ApplicationBase.EndsWith(
             Path.DirectorySeparatorChar.ToString()))
    {
        appDomainSetup.ApplicationBase += Path.DirectorySeparatorChar;
    }
}


private static T GetInstancePropertyValueViaReflection<T>(
     object instance,
     string propertyName)
{
    PropertyInfo property = GetInstancePropertyInfo(instance, propertyName);
    return (T)property.GetValue(instance);
}

private static PropertyInfo GetInstancePropertyInfo(
    object instance,
    string propertyName)
{
    Type instanceType = instance.GetType();
    PropertyInfo propertyInfo = instanceType
        .GetProperty(
            propertyName, 
            BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
    if (propertyInfo == null)
    {
        throw new ArgumentOutOfRangeException(
            nameof(propertyName),
            propertyName,
            FormattableString.Invariant(
                $"{instanceType} does not have a property '{propertyName}'"));
    }

    return propertyInfo;
}

这是在测试中创建Wpf-App之前完成的,对于我来说,它本身是通过xunit collection fixture进行范围确定的。