0

I am using cosmos db to store and fetch data. Previously I was using DocumentClient like:

 public class ProductRepository : IProductRepository
    {
        private DocumentClient _documentClient;
        private DocumentCollection _graphCollection;        

        public ProductRepository(DocumentClient documentClient, DocumentCollection graphCollection)
        {
            _documentClient = documentClient;
            _graphCollection = graphCollection;

        }

        public async Task Create(Product product)
        {
            var createQuery = CreateQuery(product);
            IDocumentQuery<dynamic> query = _documentClient.CreateGremlinQuery<dynamic>(_graphCollection, createQuery);
            if(query.HasMoreResults)
            {
                await query.ExecuteNextAsync();
            }
        }

     public async Task<Product> Get(string id)
     {
        Product product = null;
        var getQuery = @"g.V('" + id + "')";
        var query = _documentClient.CreateGremlinQuery<dynamic>(_graphCollection, getQuery);
        if (query.HasMoreResults)
        {
            var result = await query.ExecuteNextAsync();
            if (result.Count == 0)
                return product;
            var productData = (JObject)result.FirstOrDefault();
            product = new Product
            {
               name = productData["name"].ToString()
            };
        }
        return product;
     }
    }
}

But it is not unit testable so I want to convert it to IDocumentClient but IDocumentClient doesn't contain definition for CreateGremlinQuery. So what is the best possible way to convert my methods so that they will be using IDocumentClient? Do I need to use CreateDocumentQuery? if yes, how can I convert CreateGremlimQuery to CreateDocumentQuery?

Stanislav Kralin
  • 11,070
  • 4
  • 35
  • 58
Ask
  • 3,076
  • 6
  • 30
  • 63

1 Answers1

1

There are several ways to get around that. The simplest one would be to simply hard cast your IDocumentClient to DocumentClient.

If you go with that approach your code becomes:

public class ProductRepository : IProductRepository
{
    private IDocumentClient _documentClient;
    private DocumentCollection _graphCollection;        

    public ProductRepository(IDocumentClient documentClient, DocumentCollection graphCollection)
    {
        _documentClient = documentClient;
        _graphCollection = graphCollection;

    }

    public async Task Create(Product product)
    {
        var createQuery = CreateQuery(product);
        IDocumentQuery<dynamic> query = ((DocumentClient)_documentClient).CreateGremlinQuery<dynamic>(_graphCollection, createQuery);
        if(query.HasMoreResults)
        {
            await query.ExecuteNextAsync();
        }
    }

     public async Task<Product> Get(string id)
     {
        Product product = null;
        var getQuery = @"g.V('" + id + "')";
        var query = ((DocumentClient)_documentClient).CreateGremlinQuery<dynamic>(_graphCollection, getQuery);
        if (query.HasMoreResults)
        {
            var result = await query.ExecuteNextAsync();
            if (result.Count == 0)
                return product;
            var productData = (JObject)result.FirstOrDefault();
            product = new Product
            {
               name = productData["name"].ToString()
            };
        }
        return product;
    }
}

You could also create your own extensions for IDocumentClient.

public static class MoreGraphExtensions
    {
        public static IDocumentQuery<T> CreateGremlinQuery<T>(this IDocumentClient documentClient, DocumentCollection collection, string gremlinExpression, FeedOptions feedOptions = null, GraphSONMode graphSONMode = GraphSONMode.Compact)
        {
            return GraphExtensions.CreateGremlinQuery<T>((DocumentClient)documentClient, collection, gremlinExpression, feedOptions, graphSONMode);
        }

        public static IDocumentQuery<object> CreateGremlinQuery(this IDocumentClient documentClient, DocumentCollection collection, string gremlinExpression, FeedOptions feedOptions = null, GraphSONMode graphSONMode = GraphSONMode.Compact)
        {
            return GraphExtensions.CreateGremlinQuery<object>((DocumentClient)documentClient, collection, gremlinExpression, feedOptions, graphSONMode);
        }
    }

It is a pre-release however, so I do think that Microsoft will get around moving the extension methods at the interface level.

Nick Chapsas
  • 6,872
  • 1
  • 20
  • 29
  • Thanks Nick. But I think if I go with extension method approach then it would be really hard to create mock for unit test. What do you.think? – Ask Nov 15 '18 at 17:01
  • @Ask It would, but it is hard enough to do it with concrete class implementation anyway, and the IDocumentQuery is not easy to unit test to begin with. The current version of the SDK is really hard to unit test so I would go with this solution and focus my effort in integration testing. – Nick Chapsas Nov 15 '18 at 17:06
  • Thanks, do you have any good link where I can find unit test for IDocumentClient? – Ask Nov 15 '18 at 17:08
  • @Ask You can take a look one how I am unit testing my CosmosDB library, Cosmonaut. It covers most of the unit testable methods of IDocumentClient. You can find it here: https://github.com/Elfocrash/Cosmonaut/tree/develop/tests/Cosmonaut.Unit I also wrote a blog about it. You can find that here: https://chapsas.com/testing-your-cosmosdb-code-and-automating-it-with-appveyor/ – Nick Chapsas Nov 15 '18 at 17:10
  • can you tell me one more thing? I have changed my repo as you said but CreateGremlinQuery is an extension method. So is there way to create mock for CreateGremlinQuery? – Ask Nov 16 '18 at 05:53
  • There are many posts online one how you can do that. I would start here: https://stackoverflow.com/questions/2295960/mocking-extension-methods-with-moq – Nick Chapsas Nov 16 '18 at 09:16