redis-om-dotnet icon indicating copy to clipboard operation
redis-om-dotnet copied to clipboard

Making IRedisCollection<T> more unit test friendly

Open zulander1 opened this issue 1 year ago • 0 comments

I am in a scenario where we do not have access to a Redis instance when running the test. I have tried to see if I can override the IRedisConnectionProvider and return a custom IRedisCollection. However, the code in SearchExtensions is strongly typed. I have also tried using NSubstitute to return the custom class, but it resulted in the same error. I am not sure if this issue has helped successfully implemented the tests.

image

This is how to reproduce the issue

var _mockedObject = Substitute.ForPartsOf<ListConnectionProvider>();
_mockedObject.RedisCollection<Experience>(Arg.Any<bool>(), Arg.Any<int>()).Returns(f => { return new RedisCollectionTest<Experience>(); });
IRedisCollection<Experience> experiences = _mockedObject.RedisCollection<Experience>();
var filteredExperiences = await experiences.Where(x => x.Skills.Contains("C#")).ToListAsync();

[Document(StorageType = StorageType.Json, Prefixes = ["Test"], IndexName = "Test:idx")]
public class Experience
{
    [RedisIdField]
    [Indexed]
    public string Id { get; set; } = default!;

    [Indexed]
    public string[] Skills { get; set; } = default!;

    public override string ToString()
    {
        return string.Join(" ", Skills);
    }
}

public class ListConnectionProvider : IRedisConnectionProvider
{
    public IRedisConnection Connection => throw new NotImplementedException();

    public virtual RedisAggregationSet<T> AggregationSet<T>(int chunkSize = 100)
    {
        throw new NotImplementedException();
    }

    public virtual IRedisCollection<T> RedisCollection<T>(int chunkSize = 100) where T : notnull
    {
        return new RedisCollectionTest<T>();
    }

    public virtual IRedisCollection<T> RedisCollection<T>(bool saveState, int chunkSize = 100) where T : notnull
    {
        return new RedisCollectionTest<T>();
    }
}

public class RedisCollectionTest<T> : IRedisCollection<T>
{
    public IList<T> Items = new List<T>();

    public bool SaveState => throw new NotImplementedException();

    public RedisCollectionStateManager StateManager => throw new NotImplementedException();

    public int ChunkSize => throw new NotImplementedException();

    public Type ElementType => throw new NotImplementedException();

    public Expression Expression { get; init; }

    public IQueryProvider Provider => Items.AsQueryable().Provider;

    public Expression<Func<T, bool>>? BooleanExpression { get; set; }

    public bool Any()
    {
        return Items.Any();
    }

    public bool Any(Expression<Func<T, bool>> expression)
    {
        return Items.Any(expression.Compile());
    }

    public Task<bool> AnyAsync()
    {
        return Task.FromResult(Items.Any());
    }

    public Task<bool> AnyAsync(Expression<Func<T, bool>> expression)
    {
        return Task.FromResult(Items.Any(expression.Compile()));
    }

    public int Count(Expression<Func<T, bool>> expression)
    {
        return Items.Count(expression.Compile());
    }

    public int Count()
    {
        return Items.Count();
    }

    public Task<int> CountAsync()
    {
        return Task.FromResult(Items.Count());
    }

    public Task<int> CountAsync(Expression<Func<T, bool>> expression)
    {
        return Task.FromResult(Items.Count(expression.Compile()));
    }

    public void Delete(T item)
    {
        Items.Remove(item);
    }

    public void Delete(IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            Items.Remove(item);
        }
    }

    public Task DeleteAsync(T item)
    {
        Delete(item);
        return Task.CompletedTask;
    }

    public Task DeleteAsync(IEnumerable<T> items)
    {
        Delete(items);
        return Task.CompletedTask;
    }

    public T? FindById(string id)
    {
        return default;
    }

    public Task<T?> FindByIdAsync(string id)
    {
        return null;
    }

    public Task<IDictionary<string, T?>> FindByIdsAsync(IEnumerable<string> ids)
    {
        var dictionary = new Dictionary<string, T?>();
        foreach (var id in ids)
        {
            dictionary[id] = FindById(id);
        }
        return Task.FromResult<IDictionary<string, T?>>(dictionary);
    }

    public T First(Expression<Func<T, bool>> expression)
    {
        return Items.First(expression.Compile());
    }

    public Task<T> FirstAsync()
    {
        return Task.FromResult(Items.First());
    }

    public Task<T> FirstAsync(Expression<Func<T, bool>> expression)
    {
        return Task.FromResult(Items.First(expression.Compile()));
    }

    public T? FirstOrDefault(Expression<Func<T, bool>> expression)
    {
        return Items.FirstOrDefault(expression.Compile());
    }

    public Task<T?> FirstOrDefaultAsync()
    {
        return Task.FromResult(Items.FirstOrDefault());
    }

    public Task<T?> FirstOrDefaultAsync(Expression<Func<T, bool>> expression)
    {
        return Task.FromResult(Items.FirstOrDefault(expression.Compile()));
    }

    public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
    {
        throw new NotImplementedException();
    }

    public IEnumerator<T> GetEnumerator()
    {
        return Items.GetEnumerator();
    }

    public string Insert(T item)
    {
        Items.Add(item);
        return item.ToString();
    }

    public string Insert(T item, TimeSpan timeSpan)
    {
        return Insert(item);
    }

    public string? Insert(T item, WhenKey when, TimeSpan? timeSpan = null)
    {
        return Insert(item);
    }

    public Task<string> InsertAsync(T item)
    {
        return Task.FromResult(Insert(item));
    }

    public Task<string> InsertAsync(T item, TimeSpan timeSpan)
    {
        return Task.FromResult(Insert(item));
    }

    public Task<string?> InsertAsync(T item, WhenKey when, TimeSpan? timeSpan = null)
    {
        throw new NotImplementedException();
    }

    public Task<List<string>> InsertAsync(IEnumerable<T> items)
    {
        throw new NotImplementedException();
    }

    public Task<List<string>> InsertAsync(IEnumerable<T> items, TimeSpan timeSpan)
    {
        return InsertAsync(items);
    }

    public Task<List<string?>> InsertAsync(IEnumerable<T> items, WhenKey when, TimeSpan? timeSpan = null)
    {
        return InsertAsync(items);
    }

    public void Save()
    {
        // No implementation needed
    }

    public ValueTask SaveAsync()
    {
        return new ValueTask(Task.CompletedTask);
    }

    public T Single(Expression<Func<T, bool>> expression)
    {
        return Items.Single(expression.Compile());
    }

    public Task<T> SingleAsync()
    {
        return Task.FromResult(Items.Single());
    }

    public Task<T> SingleAsync(Expression<Func<T, bool>> expression)
    {
        return Task.FromResult(Items.Single(expression.Compile()));
    }

    public T? SingleOrDefault(Expression<Func<T, bool>> expression)
    {
        return Items.SingleOrDefault(expression.Compile());
    }

    public Task<T?> SingleOrDefaultAsync()
    {
        return Task.FromResult(Items.SingleOrDefault());
    }

    public Task<T?> SingleOrDefaultAsync(Expression<Func<T, bool>> expression)
    {
        return Task.FromResult(Items.SingleOrDefault(expression.Compile()));
    }

    public void Update(T item)
    {
        // No implementation needed
    }

    public Task UpdateAsync(T item)
    {
        return Task.CompletedTask;
    }

    public ValueTask UpdateAsync(IEnumerable<T> items)
    {
        return new ValueTask(Task.CompletedTask);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public Task<IList<T>> ToListAsync()
    {
        throw new NotImplementedException();
    }

    public void Update(T item, TimeSpan ttl)
    {
        throw new NotImplementedException();
    }

    public Task UpdateAsync(T item, TimeSpan ttl)
    {
        throw new NotImplementedException();
    }

    public ValueTask UpdateAsync(IEnumerable<T> items, TimeSpan ttl)
    {
        throw new NotImplementedException();
    }
}

zulander1 avatar Aug 07 '24 00:08 zulander1