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

本文关键字:Azure 查询 单元测试 Windows Moq 何使用 存根 | 更新日期: 2023-09-27 18:22:41

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

它在我的集成测试中有效,它将真正进入Azure表存储。

我想问题是对属性QueryableEntities的嘲讽,该属性从mock返回Queryable<Word>,但从ServiceContext类返回DataServiceQuery。是否可以创建一个返回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();

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

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

编辑:

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

Frederic Boer写了一篇关于如何使用表存储进行单元测试的文章,这应该会帮助你。Windows Azure存储:TDD和模拟

我知道你特别问过如何使用Moq来做这件事,我没有答案,但我发现了如何使用Fakes来做类似的事情。

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

本质上,您可以在CloudTableQuery<T>上创建一个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;
    };
}