Как настроить StaticResourceExtension при модульном тестировании расширения разметки, которое зависит от него? - PullRequest
0 голосов
/ 13 февраля 2020

Я написал собственное расширение разметки: CoalesceResourceExtension

[MarkupExtensionReturnType(typeof(object))]
public class CoalesceResourceExtension : MarkupExtension
{
    public CoalesceResourceExtension(string resources)
    {
        this.Resources = resources;
    }

    public string Separator { get; set; }

    [ConstructorArgument("resources")]
    public string Resources { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (Resources != null)
        {
            foreach (string resourceName in Resources.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries))
            {
                try
                {
                    if (new StaticResourceExtension(resourceName).ProvideValue(serviceProvider) is object resource)
                    {
                        return resource;
                    }
                }
                catch { }
            }
        }

        return null;
    }
}

Но когда я приступаю к модульному тестированию CoalesceResourceExtension, я не могу найти способ настроить StaticResourceExtension так что он найдет нужные высмеянные ресурсы. Есть ли способ, которым я могу настроить вещи для StaticResourceExtension, чтобы найти мои смоделированные ресурсы?

Ответы [ 2 ]

0 голосов
/ 20 февраля 2020
Поведение

StaticResourceExtension.ProvideValue(IServiceProvider) можно контролировать с помощью имитации IServiceProvider, переданного CoalesceResourceExtension на StaticResourceExtension, но вам также нужно будет смоделировать все службы и объекты из этих служб, которые он использует: IAmbientProvider , IXamlSchemaContextProvider, XamlSchemaContext, ResourceDictionary, XamlType, PropertyInfo и XamlMember. Я создал эти макеты, наблюдая за тем, по какому пути пошёл StaticResourceExtension, и делая строгие макеты, и наблюдая за исключениями, которые они генерировали, встречая участника, который не был настроен, и просматривая исходный код StaticResourceExtension.

Вот метод, который создает целое макетированное IServiceProvider для каждой необходимой пары ключ + значение, переданной в:

private IServiceProvider MockIServiceProviderForStaticResourceExtension(params KeyValuePair<object, object>[] resources)
{
    Mock<IServiceProvider> serviceProviderMock = new Mock<IServiceProvider>(MockBehavior.Strict);
    Mock<IXamlSchemaContextProvider> xamlSchemaContextProviderMock = new Mock<IXamlSchemaContextProvider>(MockBehavior.Strict);
    Mock<IAmbientProvider> ambientProviderMock = new Mock<IAmbientProvider>(MockBehavior.Strict);
    Mock<XamlSchemaContext> xamlSchemaContextMock = new Mock<XamlSchemaContext>(MockBehavior.Strict);
    xamlSchemaContextMock.Setup(xsc => xsc.GetXamlType(It.IsAny<Type>())).Returns((Func<Type, XamlType>)(t => GetXamlTypeForType(t, xamlSchemaContextMock.Object)));
    ambientProviderMock.Setup(ap => ap.GetAllAmbientValues(null, false, It.IsAny<IEnumerable<XamlType>>(), It.IsAny<XamlMember[]>())).Returns((Func<IEnumerable<XamlType>, bool, IEnumerable<XamlType>, XamlMember[], IEnumerable<AmbientPropertyValue>>)GetAllAmbientValuesImplementation);
    xamlSchemaContextProviderMock.Setup(xscp => xscp.SchemaContext).Returns(xamlSchemaContextMock.Object);
    serviceProviderMock.Setup(s => s.GetService(typeof(IXamlSchemaContextProvider))).Returns(xamlSchemaContextProviderMock.Object);
    serviceProviderMock.Setup(s => s.GetService(typeof(IAmbientProvider))).Returns(ambientProviderMock.Object);
    serviceProviderMock.Setup(s => s.GetService(It.IsNotIn(typeof(IXamlSchemaContextProvider), typeof(IAmbientProvider)))).Returns(null);

    return serviceProviderMock.Object;

    IEnumerable<AmbientPropertyValue> GetAllAmbientValuesImplementation(IEnumerable<XamlType> ceilingTypes, bool searchLiveStackOnly, IEnumerable<XamlType> types, params XamlMember[] properties)
    {
        Mock<ResourceDictionary> resourceDictionaryMock = new Mock<ResourceDictionary>(MockBehavior.Strict);
        resourceDictionaryMock.Protected().Setup("OnGettingValue", false, ItExpr.Is<object>(o => resources.Any(kvp => kvp.Key.Equals(o))), ItExpr.Ref<object>.IsAny, ItExpr.Ref<bool>.IsAny).CallBase();
        foreach (KeyValuePair<object, object> kvp in resources)
        {
            resourceDictionaryMock.Object.Add(kvp.Key, kvp.Value);
        }

        Mock<AmbientPropertyValue> ambientPropertyValueMock = new Mock<AmbientPropertyValue>(MockBehavior.Strict, null, resourceDictionaryMock.Object);
        return new List<AmbientPropertyValue> { ambientPropertyValueMock.Object };
    }

    XamlType GetXamlTypeForType(Type t, XamlSchemaContext xamlSchemaContext)
    {
        Mock<XamlType> xamlTypeMock = new Mock<XamlType>(MockBehavior.Strict, t, xamlSchemaContext);
        xamlTypeMock.Protected().Setup<XamlMember>("LookupMember", true, "Resources", false).Returns((Func<string, bool, XamlMember>)LookupMemberImplementation);
        xamlTypeMock.Protected().Setup<XamlMember>("LookupMember", true, "BasedOn", false).Returns((Func<string, bool, XamlMember>)LookupMemberImplementation);
        xamlTypeMock.Setup(xt => xt.ToString()).CallBase();
        return xamlTypeMock.Object;

        XamlMember LookupMemberImplementation(string name, bool skipReadOnlyCheck)
        {
            Mock<PropertyInfo> propertyInfoMock = new Mock<PropertyInfo>(MockBehavior.Strict);
            propertyInfoMock.Setup(pi => pi.Name).Returns(name);
            propertyInfoMock.Setup(pi => pi.DeclaringType).Returns(t);
            Mock<XamlMember> xamlMemberMock = new Mock<XamlMember>(MockBehavior.Strict, propertyInfoMock.Object, xamlSchemaContext);
            xamlMemberMock.Setup(xm => xm.ToString()).CallBase();
            return xamlMemberMock.Object;
        }
    }
}
0 голосов
/ 13 февраля 2020

Я столкнулся с подобной проблемой с ValueConverter / MarkupExtension, который использовал Application.Current.TryFindResource().

Я создал interface с именем IApplicationResourceResolver с определением метода для TryFindResource.

Затем я создал реализацию интерфейса ApplicationResourceResolver, которая использует Application.Current.TryFindResource().

В моем конвертере / расширении у меня есть свойство publi c:

public IApplicationResourceResolver Resolver {get; internal set; }

И в моем конструкторе я установил реализацию по умолчанию:

public StringToResourceConverter()
{
    Resolver = new ApplicationResourceResolver();
}

Теперь в моих модульных тестах я могу установить Resolver на макетированную версию для проведения тестов!

Вы должны быть в состоянии использовать этот шаблон для решения вашей проблемы. Надеюсь, это поможет.

...