簡體   English   中英

在對依賴它的標記擴展進行單元測試時,如何設置 StaticResourceExtension?

[英]How can I setup StaticResourceExtension when unit testing a markup extension that relies on it?

我編寫了一個自定義標記擴展: 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設置一些東西來找到我的模擬資源?

StaticResourceExtension.ProvideValue(IServiceProvider)的行為可以通過模擬CoalesceResourceExtension傳遞給StaticResourceExtensionIServiceProvider來控制,但您還需要模擬它使用的這些服務中的所有服務和對象: IAmbientProviderIXamlSchemaContextProviderXamlSchemaContextResourceDictionaryXamlTypePropertyInfoXamlMember 我通過觀察StaticResourceExtension所采用的路徑來構建這些模擬,方法是使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;
        }
    }
}

我在使用Application.Current.TryFindResource()ValueConverter / MarkupExtension中遇到了類似的問題。

我創建了一個interface叫做IApplicationResourceResolver與方法定義TryFindResource

然后我創建了一個名為ApplicationResourceResolver的接口的實現,它使用Application.Current.TryFindResource()

在我的轉換器/擴展中,我有一個公共屬性:

public IApplicationResourceResolver Resolver {get; internal set; }

在我的構造函數中,我設置了我的默認實現:

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

現在,在我的單元測試中,我可以將Resolver設置為模擬版本來進行測試!

您應該能夠使用此模式來解決您的問題。 我希望這有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM