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

6

我正在编写一些用于 WPF 应用程序的单元测试,尽管我试图避免这样做,但我需要测试一些实例化视图的代码。一旦视图被实例化,所有的标记扩展、样式等都会被评估。为了解决这个问题,我创建了一个虚拟的 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);
       }
    }
}

我之前尝试过这个方法,效果还可以。

但是我遇到了一个小问题。我的一些资源在pack 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'

我已将资源目录添加为部署项,并确认该图像位于TestRun输出目录中。似乎AppDomain的操作目录在我的测试程序集位置的上一级文件夹,因为该文件实际上位于:c:\Solution\TestResults\Workspace_2012-03-01 14_54_29\Out\Resources\image.png
有什么建议可以让WPF应用程序使用Out目录作为其主文件夹吗?

手动强制设置AppDomain的基础文件夹是否有效?AppDomain.CurrentDomain.SetData("APPBASE", "FolderNameHere");似乎有更好的方法,但我的记忆正在衰退。这可能已经足够了(商标)。 - ianschol
1
挖掘了几个小时。AppDomain.CurrentDomain.BaseDirectory 返回 Out 文件夹。 - bryanbcook
2个回答

5
这是因为测试运行程序设置的 AppDomain.BaseDirectory 没有带上末尾的 '/' 字符,这会导致解析 siteoforigin 路径的代码丢失路径中的最后一个目录。
您可以通过在正常或测试运行时查看以下代码的结果来检查此问题。
Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory);

最近在NUnit中(包括2.6版本)已经修复,但在其他测试运行器中可能仍然存在问题。

如果您感兴趣,这相当于siteoforigin代码的等效操作:

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

baseDirectory中尝试带斜杠和不带斜杠的情况。

是的,尾随斜杠是罪魁祸首。我发帖后不久就发现了这一点,但我还没有回来添加我的发现,但无论如何我会接受你的答案。实际上,我正在使用2010年的MSTest,它也不会放置尾随斜杠。 - bryanbcook

1

我已经成功解决了问题,如上面的答案所述,通过反射更改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://dev59.com/HWHVa4cB1Zd3GeqPjBC3#9908449
    // - 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 进行作用域限定的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接