Модульное тестирование приложения WPF с пакетом siteoforigin Uri

У меня есть несколько модульных тестов, которые я пишу для приложения WPF, и, как бы я ни старался этого избежать, у меня есть тестируемый код, который создает экземпляр представления. Как только создается экземпляр представления, оцениваются все расширения разметки, стили и т. д. Чтобы решить эту проблему, я создал фиктивное Приложение и зарегистрировал все необходимые ресурсы при инициализации тестовой сборки:

[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://siteoforigin: в пакете Uri, и когда тесты создают экземпляр этого представления, я получаю сообщение об ошибке о невозможности разрешения файла.

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 в качестве основной папки?


person bryanbcook    schedule 01.03.2012    source источник
comment
Работает ли принудительная установка базовой папки вашего Appdomain вручную? AppDomain.CurrentDomain.SetData("APPBASE", "FolderNameHere"); Вроде есть способ получше, но память меня подводит. Это может быть достаточно хорошо (тм).   -  person ianschol    schedule 02.03.2012
comment
Копался в этом часами. AppDomain.CurrentDomain.BaseDirectory возвращает папку Out.   -  person bryanbcook    schedule 02.03.2012


Ответы (2)


Это связано с тем, что AppDomain.BaseDirectory, настроенный вашим исполнителем тестов, не имеет завершающего символа '/', это приводит к тому, что код, который разрешает пути siteoforigin, теряет последний каталог в пути.

Вы можете проверить это, взглянув на результат следующего кода при нормальной работе или в тестах.

Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory);

Недавно это было исправлено в NUnit (включено в версию 2.6). , но все еще может быть проблемой с другими тестировщиками.

Если вам интересно, это эквивалент того, что делает код siteoforigin:

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

Попробуйте сделать это с косой чертой в конце baseDirectory и без нее.

person Leaf Garland    schedule 28.03.2012
comment
Да, косая черта в конце виновата. Я обнаружил это вскоре после публикации и не вернулся, чтобы добавить свои выводы, но я все равно приму ваш ответ. На самом деле я использую MSTest в 2010 году, который также не ставит косую черту. - person bryanbcook; 30.03.2012

Я успешно обошел проблему, как описано в ответе выше, изменив 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-приложения в тесте, которое само, в моем случае, ограничено через xunit. приспособление для коллекции.

person BatteryBackupUnit    schedule 20.08.2020