lundi 1 avril 2019

Mocking Unit tests and dependency injection for static async class

I am having a static async cache for contacts list. Inside of the cache I am calling my repository to get data from backend. I want to mock ContactsRepository, but I need to pass repository as a parameter and use dependency injection.

According to the documentation it won't work, because i need an instance of class to use dependency injection.

public interface IContactsCache
{
    Task<List<Contact>> GetContactsAsync(int inst, CancellationToken ct);
}

public class ContactsCache : IContactsCache
{
    private static readonly object _syncRoot = new object();
    private static readonly Dictionary<int, Task<List<Contact>>> _contactsTasks = new Dictionary<int, Task<List<Contact>>>();

    public static Task<List<Contact>> GetContactsAsync(int inst)
    {
        return GetContactsAsync(inst, CancellationToken.None);
    }

    public static async Task<List<Contact>> GetCodeValuesAsync(int inst, CancellationToken ct)
    {
        Task<List<Contact>> task;

        lock (_syncRoot)
        {
            if (_contactsTasks.ContainsKey(inst) && (_contactsTasks[inst].IsCanceled || _contactsTasks[inst].IsFaulted))
            {
                _contactsTasks.Remove(inst);
            }

            if (!_contactsTasks.ContainsKey(inst))
            {
                _contactsTasks[inst] = Task.Run(async () =>
                {
                    using (var rep = new ContactsRepository())
                    {
                        return await rep.LoadAsync(inst, ct).ConfigureAwait(false);
                    }
                });
            }

            task = _contactsTasks[inst];
        }

        var res = await task.ConfigureAwait(false);

        lock (_syncRoot)
        {
            return res != null ? res.ToList() : null;
        }
    }

    Task<List<CodeValue>> IContactsCache.GetContactsAsync(int inst, CancellationToken ct)
    {
        return GetContactsAsync(inst, ct);
    }
}

At the end I expect to have this kind of usage, but I can't figure out how to change cache class or any king of other help will be very helpful.

[TestMethod]
public async void GetContactAsync_WhenCalled_ReturnCodeValuesCache()
{
    var expected = new List<Contact>
    {
        new Contact() {Instance = 1, Name = "Test" }
    };

    var mock = new Mock<IContactsRepository>()
        .Setup(x => x.LoadAsync(It.IsAny<int>(), CancellationToken.None))
        .ReturnsAsync(new List<Contact>(expected));

    var actual = await ContactsCache.GetContactsAsync(It.IsAny<int>(), CancellationToken.None);

    CollectionAssert.AreEqual(actual, expected);
}

But it can't work and I don't know how to properly write unit test.

I have a lot of such caches where I am using such repositories. Are there any standart or best practise how to unit test static async caches and how to mock repository in this case?

Aucun commentaire:

Enregistrer un commentaire