![](/img/trans.png)
[英]Markup extension 'StaticResourceExtension' requires 'IXamlSchemaContextProvider' be implemented in the IServiceProvider for ProvideValue
[英]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
傳遞給StaticResourceExtension
的IServiceProvider
來控制,但您還需要模擬它使用的這些服務中的所有服務和對象: IAmbientProvider
、 IXamlSchemaContextProvider
、 XamlSchemaContext
、 ResourceDictionary
、 XamlType
、 PropertyInfo
和XamlMember
。 我通過觀察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.