如何使用 Moq 对带有存根的 Windows Azure 表查询进行单元测试?

发布于 2025-01-02 10:38:06 字数 3727 浏览 1 评论 0原文

我无法让我的单元测试正常工作。

它在我进行的集成测试中有效,它实际上会到达 Azure 表存储。

我猜的问题是对属性 QueryableEntities 的模拟,它从模拟返回一个 Queryable 但它返回一个 DataServiceQuery 来自 ServiceContext 类。是否可以创建返回 Queryable 的 DataServiceQuery 类型的存根?

这是我的代码:

测试

[TestMethod]
    public void GetAExistingWordInStorageShouldReturnCorrectWord()
    {

        Word expected = new Word(Dictionaries.Swedish.ToString(), "Word", "Word");

        List<Word> Words = new List<Word>();
        Words.Add(new Word(Dictionaries.Swedish.ToString(), "Word", "Word"));

        IQueryable<Word> WordQueryable = Words.AsQueryable<Word>();
        
        var mock = new Mock<IServiceContext<Word>>();
        mock.Setup(x => x.QueryableEntities).Returns(WordQueryable);

        DictionaryRepository dr = new DictionaryRepository(Models.Dictionaries.Swedish, "testdictionaries");
        dr.Context = mock.Object;

        Word result = dr.GetWord(expected.Text, false);

        Assert.AreEqual(expected, result);
    }

IServiceContect 接口

public interface IServiceContext<TEntity>
{
    IQueryable<TEntity> QueryableEntities {get;}
}

ServiceContext 类

public class ServiceContext<TEntity> : TableServiceContext, IServiceContext<TEntity> where TEntity : TableServiceEntity
{

    private readonly string tableName;

    public ServiceContext(CloudStorageAccount account, String tableName)
        : base(account.TableEndpoint.ToString(), account.Credentials)
    {
        this.tableName = tableName;
        this.IgnoreResourceNotFoundException = true;
    }

    public IQueryable<TEntity> QueryableEntities
    {
        get
        {
            return CreateQuery<TEntity>(tableName);
        }
    }

}

字典存储库

     public class DictionaryRepository : IDictionaryRepository
{
    public Dictionaries Dictionary { get; set; }
    public String TableName;

    public IServiceContext<Word> Context;

    public DictionaryRepository(Dictionaries dictionary)
        : this(dictionary, "dictionaries")
    {
    }

    public DictionaryRepository(Dictionaries dictionary, String tableName)
    {
        Dictionary = dictionary;
        this.TableName = tableName;
        CloudStorageAccount account = CloudStorageAccount.Parse(***);
        Context = new ServiceContext<Word>(account, this.TableName);
    }

    public List<Tile> GetValidTiles()
    {
        throw new NotImplementedException();
    }

    public Type ResolveEntityType(String name)
    {
        return typeof(Word);
    }

    public Word GetWord(string word, Boolean useCache = false)
    {
      
        var q = this.Context.QueryableEntities.Where(x => x.PartitionKey == Dictionary.ToString() && x.RowKey == word).AsTableServiceQuery();

        Word result = q.Execute().SingleOrDefault();

        if (result == null)
            return null;

        return result;

    }} 

我收到以下错误

错误:

    ArgumentNullException was unhandled by user code
    Value cannot be null.
    Parameter name: query

在 DictionaryRepository 类中的以下行调用 .AsTableServiceQuery() 时出现错误:

var q = this.Context.QueryableEntities.Where(x => x.PartitionKey == Dictionary.ToString() && x.RowKey == word).AsTableServiceQuery();

I can't get my unit test to work properly.

It works in a integration test I have where it will actually hit the Azure Table Storage.

The problem I guess is the mocking of the property QueryableEntities which returns a Queryable<Word> from the mock but it returns a DataServiceQuery from the ServiceContext class. Is it possible to create a stub of type DataServiceQuery which returns a Queryable?

This is my code:

Test

[TestMethod]
    public void GetAExistingWordInStorageShouldReturnCorrectWord()
    {

        Word expected = new Word(Dictionaries.Swedish.ToString(), "Word", "Word");

        List<Word> Words = new List<Word>();
        Words.Add(new Word(Dictionaries.Swedish.ToString(), "Word", "Word"));

        IQueryable<Word> WordQueryable = Words.AsQueryable<Word>();
        
        var mock = new Mock<IServiceContext<Word>>();
        mock.Setup(x => x.QueryableEntities).Returns(WordQueryable);

        DictionaryRepository dr = new DictionaryRepository(Models.Dictionaries.Swedish, "testdictionaries");
        dr.Context = mock.Object;

        Word result = dr.GetWord(expected.Text, false);

        Assert.AreEqual(expected, result);
    }

IServiceContect interface

public interface IServiceContext<TEntity>
{
    IQueryable<TEntity> QueryableEntities {get;}
}

ServiceContext Class

public class ServiceContext<TEntity> : TableServiceContext, IServiceContext<TEntity> where TEntity : TableServiceEntity
{

    private readonly string tableName;

    public ServiceContext(CloudStorageAccount account, String tableName)
        : base(account.TableEndpoint.ToString(), account.Credentials)
    {
        this.tableName = tableName;
        this.IgnoreResourceNotFoundException = true;
    }

    public IQueryable<TEntity> QueryableEntities
    {
        get
        {
            return CreateQuery<TEntity>(tableName);
        }
    }

}

Dictionary Repository

     public class DictionaryRepository : IDictionaryRepository
{
    public Dictionaries Dictionary { get; set; }
    public String TableName;

    public IServiceContext<Word> Context;

    public DictionaryRepository(Dictionaries dictionary)
        : this(dictionary, "dictionaries")
    {
    }

    public DictionaryRepository(Dictionaries dictionary, String tableName)
    {
        Dictionary = dictionary;
        this.TableName = tableName;
        CloudStorageAccount account = CloudStorageAccount.Parse(***);
        Context = new ServiceContext<Word>(account, this.TableName);
    }

    public List<Tile> GetValidTiles()
    {
        throw new NotImplementedException();
    }

    public Type ResolveEntityType(String name)
    {
        return typeof(Word);
    }

    public Word GetWord(string word, Boolean useCache = false)
    {
      
        var q = this.Context.QueryableEntities.Where(x => x.PartitionKey == Dictionary.ToString() && x.RowKey == word).AsTableServiceQuery();

        Word result = q.Execute().SingleOrDefault();

        if (result == null)
            return null;

        return result;

    }} 

I'm getting the following error

Error:

    ArgumentNullException was unhandled by user code
    Value cannot be null.
    Parameter name: query

I get the error when calling .AsTableServiceQuery() on the following line in DictionaryRepository class:

var q = this.Context.QueryableEntities.Where(x => x.PartitionKey == Dictionary.ToString() && x.RowKey == word).AsTableServiceQuery();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

伴我心暖 2025-01-09 10:38:06

您没有提到您遇到的错误,但由于 QueryableEntities 是只读属性,请尝试使用 mock.SetupGet 而不是 mock.Setup >。

编辑:

进一步研究问题是 .AsTableServiceQuery() 扩展方法尝试将 IQueryable 转换为 DataServiceQuery code>,失败导致 null 异常。

Frederic Boerr 发表了一篇关于如何使用表存储进行单元测试的文章,应该可以帮助您。 Windows Azure 存储:TDD 和模拟< /a>

You haven't mentioned the error you're getting, but since the QueryableEntities is a readonly property try using mock.SetupGet instead of mock.Setup.

EDIT:

Looking into it further the problem is that the .AsTableServiceQuery() extension method attempts to cast the IQueryable<T> to a DataServiceQuery<T>, which fails causing the null exception.

There's a post by Frederic Boerr about how to do unit testing with table storage that should help you out. Windows Azure Storage: TDD and mocks

梨涡少年 2025-01-09 10:38:06

我知道您特别问过如何使用 Moq 来做到这一点,我没有答案,但我想出了如何使用 Fakes 来做类似的事情。

http://azurator.blogspot.com/2013 /07/unit-testing-azure-table-storage-queries.html

本质上,您可以在 CloudTableQuery 上创建 Shim它读取查询正在使用的 Expression 对象,并使用如下代码将相同的逻辑应用于您的 IEnumerable:

[TestMethod]
public void here_is_my_test()
{
    IEnumerable<MyEntityType> fakeResults = GetFakeResults();

    using (ShimsContext.Create())
    {
        InterceptCloudTableQueryExecute<MyEntityType>(fakeResults);

        DoQuery();

        AssertStuff();
    }
}

public void InterceptCloudTableQueryExecute<T>(IEnumerable<T> result)
{
    var query = result.AsQueryable();

    ShimCloudTableQuery<T>.AllInstances.Execute = (instance) =>
    {
        // Get the expression evaluator.
        MethodCallExpression ex = (MethodCallExpression)instance.Expression;

        // Depending on how I called CreateQuery, sometimes the objects
        // I need are nested one level deep.
        if (ex.Arguments[0] is MethodCallExpression)
        {
            ex = (MethodCallExpression)ex.Arguments[0];
        }

        UnaryExpression ue = ex.Arguments[1] as UnaryExpression;

        // Get the lambda expression
        Expression<Func<T, bool>> le = ue.Operand as Expression<Func<T, bool>>;

        query = query.Where(le);
        return query;
    };
}

I know you specifically asked how to do this using Moq, and I don't have an answer to that, but I figured out how to do something similar using Fakes.

http://azurator.blogspot.com/2013/07/unit-testing-azure-table-storage-queries.html

Essentially you can create a Shim on CloudTableQuery<T> that reads the Expression object the query is using and applies that same logic to your IEnumerable using code like this:

[TestMethod]
public void here_is_my_test()
{
    IEnumerable<MyEntityType> fakeResults = GetFakeResults();

    using (ShimsContext.Create())
    {
        InterceptCloudTableQueryExecute<MyEntityType>(fakeResults);

        DoQuery();

        AssertStuff();
    }
}

public void InterceptCloudTableQueryExecute<T>(IEnumerable<T> result)
{
    var query = result.AsQueryable();

    ShimCloudTableQuery<T>.AllInstances.Execute = (instance) =>
    {
        // Get the expression evaluator.
        MethodCallExpression ex = (MethodCallExpression)instance.Expression;

        // Depending on how I called CreateQuery, sometimes the objects
        // I need are nested one level deep.
        if (ex.Arguments[0] is MethodCallExpression)
        {
            ex = (MethodCallExpression)ex.Arguments[0];
        }

        UnaryExpression ue = ex.Arguments[1] as UnaryExpression;

        // Get the lambda expression
        Expression<Func<T, bool>> le = ue.Operand as Expression<Func<T, bool>>;

        query = query.Where(le);
        return query;
    };
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文