2015-12-04 69 views
8

我在服務器上使用Web Api OData v4,在客戶端上使用OData Client code generator。它工作正常,但我不知道如何測試客戶端上的代碼。如何對OData客戶端進行單元測試?

在服務器上我公開了一個「級別」dbSet。

這裏的客戶端上的代碼片段:

public class LevelViewer 
{ 
    public virtual ODataContainer Container{get;set;} //t4 template generated 

    public LevelViewer(ODataContainer container=null) 
    { 
     if(container==null) 
     { 
      Container=new ODataContainer(new Uri("http://blabla")); 
     } 
    } 

    //I want to test this (actually there are more things, this is an example) 
    public List<Level> GetRootLevels() 
    { 
     return ODataContainer.Levels.Where(l=>l.IsRoot).ToList(); 
    } 
} 

我接受,爲了能夠以某種方式模擬它由T4模板作爲構造函數的參數生成的OData容器。

單元測試,這裏就是我迷路了:

[TestMethod] 
    public void LevelsVMConstructorTest() 
    { 
     List<Level>levels=new List<Level>(); 
     levels.Add(new Level(){Id=1,LevelId=1,Name="abc",IsRoot=True}); 
     IQueryable<Level>levelsIQ=levels.AsQueryable<Level>(); 

     //? 
     var odataContainerMock=new Mock<ODataContainer>(); 
     odataContainerMock.Setup(m=>m.Levels).Returns(I DON'T KNOW); 


     //I want to get here 
     LevelViewer lv = new LevelViewer(odataContainerMock.Object); 
     Assert.IsTrue(lv.GetRootLevels().Any()); 
    } 

所以在這個單元測試我只是想測試GetRootLevels方法內部的邏輯,我不想做一個集成測試或自我託管服務,我只想用內存數據來測試該方法。

如何模擬實際上是DataServiceContext類的OData客戶端生成的類?

我使用起訂量,但它可以是任何東西,(免費或至少包含在VS專業版)

編輯:這裏是ODataContainer的實現(記住,這是通過OData的客戶端自動生成)

public partial class ODataContainer : global::Microsoft.OData.Client.DataServiceContext 
{ 
    /// <summary> 
    /// Initialize a new ODataContainer object. 
    /// </summary> 
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
    public ODataContainer(global::System.Uri serviceRoot) : 
      base(serviceRoot, global::Microsoft.OData.Client.ODataProtocolVersion.V4) 
    { 
     this.ResolveName = new global::System.Func<global::System.Type, string>(this.ResolveNameFromType); 
     this.ResolveType = new global::System.Func<string, global::System.Type>(this.ResolveTypeFromName); 
     this.OnContextCreated(); 
     this.Format.LoadServiceModel = GeneratedEdmModel.GetInstance; 
     this.Format.UseJson(); 
    } 
    partial void OnContextCreated(); 
    /// <summary> 
    /// Since the namespace configured for this service reference 
    /// in Visual Studio is different from the one indicated in the 
    /// server schema, use type-mappers to map between the two. 
    /// </summary> 
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
    protected global::System.Type ResolveTypeFromName(string typeName) 
    { 
     global::System.Type resolvedType = this.DefaultResolveType(typeName, "WebServiceOData", "Constraint_Data_Feed.WebServiceOData"); 
     if ((resolvedType != null)) 
     { 
      return resolvedType; 
     } 
     resolvedType = this.DefaultResolveType(typeName, "DAL.Models", "Constraint_Data_Feed.DAL.Models"); 
     if ((resolvedType != null)) 
     { 
      return resolvedType; 
     } 
     return null; 
    } 
    /// <summary> 
    /// Since the namespace configured for this service reference 
    /// in Visual Studio is different from the one indicated in the 
    /// server schema, use type-mappers to map between the two. 
    /// </summary> 
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
    protected string ResolveNameFromType(global::System.Type clientType) 
    { 
     global::Microsoft.OData.Client.OriginalNameAttribute originalNameAttribute = (global::Microsoft.OData.Client.OriginalNameAttribute)global::System.Linq.Enumerable.SingleOrDefault(global::Microsoft.OData.Client.Utility.GetCustomAttributes(clientType, typeof(global::Microsoft.OData.Client.OriginalNameAttribute), true)); 
     if (clientType.Namespace.Equals("Constraint_Data_Feed.WebServiceOData", global::System.StringComparison.Ordinal)) 
     { 
      if (originalNameAttribute != null) 
      { 
       return string.Concat("WebServiceOData.", originalNameAttribute.OriginalName); 
      } 
      return string.Concat("WebServiceOData.", clientType.Name); 
     } 
     if (clientType.Namespace.Equals("Constraint_Data_Feed.DAL.Models", global::System.StringComparison.Ordinal)) 
     { 
      if (originalNameAttribute != null) 
      { 
       return string.Concat("DAL.Models.", originalNameAttribute.OriginalName); 
      } 
      return string.Concat("DAL.Models.", clientType.Name); 
     } 
     return null; 
    } 
    /// <summary> 
    /// There are no comments for Levels in the schema. 
    /// </summary> 
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
    [global::Microsoft.OData.Client.OriginalNameAttribute("Levels")] 
    public global::Microsoft.OData.Client.DataServiceQuery<global::Constraint_Data_Feed.DAL.Models.Level> Levels 
    { 
     get 
     { 
      if ((this._Levels == null)) 
      { 
       this._Levels = base.CreateQuery<global::Constraint_Data_Feed.DAL.Models.Level>("Levels"); 
      } 
      return this._Levels; 
     } 
    } 
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
    private global::Microsoft.OData.Client.DataServiceQuery<global::Constraint_Data_Feed.DAL.Models.Level> _Levels; 
    /// <summary> 
    /// There are no comments for Levels in the schema. 
    /// </summary> 
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
    public void AddToLevels(global::Constraint_Data_Feed.DAL.Models.Level level) 
    { 
     base.AddObject("Levels", level); 
    } 
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
    private abstract class GeneratedEdmModel 
    { 
     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
     private static global::Microsoft.OData.Edm.IEdmModel ParsedModel = LoadModelFromString(); 
     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
     private const string Edmx = @"<edmx:Edmx Version=""4.0"" xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx""> 
    <edmx:DataServices> 
    <Schema Namespace=""DAL.Models"" xmlns=""http://docs.oasis-open.org/odata/ns/edm""> 
     <EntityType Name=""Level""> 
     <Key> 
      <PropertyRef Name=""Id"" /> 
     </Key> 
     <Property Name=""Id"" Type=""Edm.Int32"" Nullable=""false"" /> 
     <Property Name=""Name"" Type=""Edm.String"" Nullable=""false"" /> 
     <Property Name=""LevelId"" Type=""Edm.Int32"" /> 
     <NavigationProperty Name=""Sublevels"" Type=""Collection(DAL.Models.Level)"" /> 
     <NavigationProperty Name=""Machines"" Type=""Collection(DAL.Models.Machine)"" /> 
     </EntityType> 
     <EntityType Name=""Machine""> 
     <Key> 
      <PropertyRef Name=""Id"" /> 
     </Key> 
     <Property Name=""Id"" Type=""Edm.Int32"" Nullable=""false"" /> 
     <Property Name=""Name"" Type=""Edm.String"" Nullable=""false"" /> 
     <Property Name=""LevelId"" Type=""Edm.Int32"" /> 
     <NavigationProperty Name=""Level"" Type=""DAL.Models.Level""> 
      <ReferentialConstraint Property=""LevelId"" ReferencedProperty=""Id"" /> 
     </NavigationProperty> 
     <NavigationProperty Name=""Parts"" Type=""Collection(DAL.Models.Part)"" /> 
     </EntityType> 
     <EntityType Name=""Part""> 
     <Key> 
      <PropertyRef Name=""Id"" /> 
     </Key> 
     <Property Name=""Id"" Type=""Edm.Int32"" Nullable=""false"" /> 
     <Property Name=""Name"" Type=""Edm.String"" Nullable=""false"" /> 
     <NavigationProperty Name=""Machines"" Type=""Collection(DAL.Models.Machine)"" /> 
     </EntityType> 
    </Schema> 
    <Schema Namespace=""WebServiceOData"" xmlns=""http://docs.oasis-open.org/odata/ns/edm""> 
    <EntityContainer Name=""ODataContainer""> 
     <EntitySet Name=""Levels"" EntityType=""DAL.Models.Level""> 
      <NavigationPropertyBinding Path=""Sublevels"" Target=""Levels"" /> 
     </EntitySet> 
    </EntityContainer> 
    </Schema> 
    </edmx:DataServices> 
</edmx:Edmx>"; 


     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
     public static global::Microsoft.OData.Edm.IEdmModel GetInstance() 
     { 
      return ParsedModel; 
     } 
     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
     private static global::Microsoft.OData.Edm.IEdmModel LoadModelFromString() 
     { 
      global::System.Xml.XmlReader reader = CreateXmlReader(Edmx); 
      try 
      { 
       return global::Microsoft.OData.Edm.Csdl.EdmxReader.Parse(reader); 
      } 
      finally 
      { 
       ((global::System.IDisposable)(reader)).Dispose(); 
      } 
     } 
     [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.OData.Client.Design.T4", "2.4.0")] 
     private static global::System.Xml.XmlReader CreateXmlReader(string edmxToParse) 
     { 
      return global::System.Xml.XmlReader.Create(new global::System.IO.StringReader(edmxToParse)); 
     } 
    } 
}  
+2

你可以確保永遠不會收到一個空DefaultContainer測試使用此構造函數的類。然後,將參數更改爲使用接口(IDefaultContainer或類似的東西)。然後,您可以通過比較返回對象來測試GetRootLevels,無論您在模擬類中設置了什麼。 – dcidral

+0

謝謝,我會試一試。 – Tuco

+0

爲了這個工作,我必須模擬Levels屬性,它是一個DataServiceQuery ,它只有getter。我仍在考慮 – Tuco

回答

7

0123'的C'tor是私人的,因此我不能用Moq來嘲笑它。

我以前MsFakes作爲一個免費的代碼編織工具來解決這個問題:

[TestMethod] 
public void LevelsVMConstructorTest() 
{ 
    using (ShimsContext.Create()) 
    { 
     List<Level> levels = new List<Level>(); 
     levels.Add(new Level() { Id = 1, LevelId = 1, Name = "abc", IsRoot = true }); 
     var levelsIQ = levels.AsQueryable(); 

     var fakeDataServiceQuery = new System.Data.Services.Client.Fakes.ShimDataServiceQuery<Level>(); 

     fakeDataServiceQuery.ProviderGet =() => levelsIQ.Provider; 
     fakeDataServiceQuery.ExpressionGet =() => levelsIQ.Expression; 
     fakeDataServiceQuery.ElementTypeGet =() => levelsIQ.ElementType; 
     fakeDataServiceQuery.GetEnumerator = levelsIQ.GetEnumerator; 

     var defaultContainerMock = new Mock<DefaultContainer>(); 
     defaultContainerMock.Setup(m => m.Levels).Returns(fakeDataServiceQuery); 

     LevelViewer lv = new LevelViewer(odataContainerMock.Object); 
     Assert.IsTrue(lv.GetRootLevels().Any()); 

    } 
} 
+0

謝謝,我會試試看,我會讓你知道結果。 – Tuco

+0

這應該是答案,但似乎[我需要VS高級版啓用MSFakes](http://visualstudio.uservoice。com/forums/121579-visual-studio/suggestions/2919309-provide-microsoft-fakes-with-all-visual-studio-edi)和我有專業版,我會嘗試獲得高級版的副本,我已經提出了一個測試,我已經upvoted這個答案 – Tuco

+0

@ATM啊我不知道這個溢價問題...你可以添加一個虛擬財產,將公開'IEnumerable '然後在你的代碼中使用它:'公共虛擬IEnumerable GetLevels {get {return Levels;}}'。使用這種方法,您不需要任何代碼編織工具。順便說一句,我在我的測試中將'Levels'標記爲虛擬方法... –

相關問題