如何测试包含Application.Current的方法

时间:2019-07-01 12:37:00

标签: c# unit-testing mvvm xamarin.forms

很少有具有Application.Current.Properties和Application.Current.SavePropertiesAsync方法的方法。

那么我该如何测试其中包含这两种方法?在尝试为它们使用Unity容器后,我陷入了困境,但它仅适用于Properties,而不是SavePropertiesAsync

我该如何实施?

我将其实现为:

public interface IAppProperties { IDictionary<string, object> Properties { get; set; } }

public class AppProperty:IAppProperties
{
    public const string AppPropertiesName = "AppProperties";

    public IDictionary<string, object> Properties { get; set; }

    public AppProperty(IDictionary<string, object> appProperties)
    {
        Properties = appProperties;
    }
}

在App XAML.cs中

UnityContainer container = new UnityContainer(); 
if (!IsUnitTestCase) 
{ 
    container.RegisterInstance<IDictionary<string, object>>(AppProperty.AppPropertiesName, Application.Current.Properties); 
} 
else 
{ 
    container.RegisterInstance<IDictionary<string, object>>(AppProperty.AppPropertiesName, new Dictionary<string,object>()); 
} 
container.RegisterType<IAppProperties,AppProperty>();
Application.Current.Resources.Add("Unity", container);

1 个答案:

答案 0 :(得分:2)

如果一个类直接依赖于Application.Current,那么您将无法对其进行测试。但是看起来您已经在依赖抽象了。

假设您需要做三件事:

  • 检索属性
  • 设置属性
  • 保存所有属性

您可以定义代表这些行为的抽象:

public interface IApplicationProperties
{
    object GetProperty(string key);
    void SetProperty(string key, object value);
    Task SavePropertiesAsync();
}

您的默认实现可能看起来像这样(尽管有很多改进的余地。)

public class ApplicationProperties : IApplicationProperties
{
    private readonly Application _application;

    public ApplicationProperties(Application application)
    {
        _application = application;
    }

    public object GetProperty(string key)
    {
        // or whatever behavior you want when the key is missing
        return _application.Properties.TryGetValue(key, out object result) ? result : null;
    }

    public void SetProperty(string key, object value)
    {
        _application.Properties[key] = value;
    }

    public async Task SavePropertiesAsync()
    {
        await _application.SavePropertiesAsync();
    }
}

此类可能取决于Application.Current,也可以将Application注入其中。

这可以从更好的类型检查以及可能限制/定义可以读取和设置的设置中受益。但是,它允许您同时通过抽象访问Application的行为,同时模拟单元测试的抽象。您可以使用Moq或只编写一个简单的测试双倍来在测试中使用。

以下是对包含测试双打的方法的调整:

// base class
public abstract class ApplicationPropertiesBase : IApplicationProperties
{
    protected abstract IDictionary<string, object> Properties { get; }

    public object GetProperty(string key)
    {
        return Properties.TryGetValue(key, out object result) ? result : null;
    }

    public void SetProperty(string key, object value)
    {
        Properties[key] = value;
    }

    public abstract Task SavePropertiesAsync();
}

// inject this
public class ApplicationProperties : ApplicationPropertiesBase
{
    private readonly Application _application;

    public ApplicationProperties(Application application)
    {
        _application = application;
    }

    protected override IDictionary<string, object> Properties => _application.Properties;

    public override async Task SavePropertiesAsync()
    {
        await _application.SavePropertiesAsync();
    }
}

// use for tests 
public class ApplicationPropertiesTestDouble : ApplicationPropertiesBase
{
    private readonly IDictionary<string, object> properties = 
        new Dictionary<string, object>();

    protected override IDictionary<string, object> Properties => properties;

    public override async Task SavePropertiesAsync()
    { }
}