2012-02-05 62 views
5

我无法让我的单元测试正常工作。 它在一个集成测试中工作,我有它将实际击中Azure表存储。 我想这个问题是物业QueryableEntities至极的嘲讽retruns从模拟可查询的,但它返回从ServiceContext类DataServiceQuery。是否有可能创建一个类型为的存根DataServiceQuery这个返回一个Queryable?如何使用Moq单元测试Windows Azure表查询与存根(Stub)?

这是我的代码:

测试

[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 unhandeled by user code 
    Value cannot be null. 
    Parameter name: query 

调用.AsTableServiceQuery(当我得到的错误)在下面一行中DictionaryRepository类:

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

考虑包装的微软的源代码在你自己的类中实现一个简化的接口。 – TrueWill 2012-02-09 13:55:50

回答

1

您没有提到的错误你得到,但是由于QueryableEntities是一个只读属性,使用mock.SetupGet代替mock.Setup尝试。

编辑:

寻找到它进一步的问题是,.AsTableServiceQuery()扩展方法试图施放IQueryable<T>DataServiceQuery<T>,其失败导致空例外。

Frederic Boerr发表了一篇关于如何使用表格存储进行单元测试的文章,该文章可以帮助您解决问题。 Windows Azure Storage: TDD and mocks

+0

没有工作。我更新了我收到的错误的问题。 – Frej 2012-02-09 13:03:34

0

我知道你明确地问过如何用Moq做这个,我没有答案,但我想通过使用Fakes来做类似的事情。

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

从本质上讲,你可以创建CloudTableQuery<T>一个垫片,它读取查询使用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; 
    }; 
}