Reputation: 11
There are few methods which have Application.Current.Properties and Application.Current.SavePropertiesAsync
methods.
So how do I test methods having these two in them? I'm stuck after trying to use Unity container for them but its only working for Properties not SavePropertiesAsync
.
How can I implement it?
I have implemented it as:
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;
}
}
In 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);
Upvotes: 1
Views: 1001
Reputation: 29282
If a class depends directly on Application.Current
then you can't test it. But it looks like you're already on track with depending on an abstraction.
Suppose there are three things you need to be able to do:
You can define an abstraction that represents those behaviors:
public interface IApplicationProperties
{
object GetProperty(string key);
void SetProperty(string key, object value);
Task SavePropertiesAsync();
}
Your default implementation could look like this (although there's plenty of room for improvement.)
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();
}
}
This class could either depend on Application.Current
or you could inject the Application
into it.
This could benefit from better type checking and perhaps limiting/defining what settings can be read and set. But it allows you to both access the behaviors of Application
through an abstraction while mocking the abstraction for unit tests. You could use Moq
or just write a simple test double to use in tests.
Here's a tweak to the approach that includes a test double:
// 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()
{ }
}
Upvotes: 2