2015-12-04 24 views
8

Sto usando Web Api OData v4 sul server e OData Client code generator sul client. Funziona bene, ma non so come testare il codice sul client.Come testare il client OData?

Sul server espongo un "Levels" dbSet.

Ecco un frammento di codice sul client:

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(); 
    } 
} 

sto accettando il contenitore odata generato dal modello T4 come parametro per il costruttore, al fine di essere in grado di Mock in qualche modo.

test di unità, qui è dove mi sono perso:

[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()); 
    } 

Quindi, in questo test di unità voglio solo per testare la logica all'interno del metodo GetRootLevels, non voglio fare un test di integrazione o di una servizio di self hosting, voglio solo testare il metodo con i dati in memoria.

Come faccio a prendere in giro la classe generata dal client OData che è in realtà una classe DataServiceContext?

sto usando Moq, ma può essere qualsiasi cosa, (libero o almeno inclusi in VS edizione professionale)

Edit: Ecco l'implementazione di ODataContainer (ricordate che questo è generata automaticamente dal cliente 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

Si potrebbe garantire di non ricevere nulla DefaultContainer testare le classi che usano questo costruttore. Quindi, modificare il parametro per utilizzare un'interfaccia (IDefaultContainer o qualcosa di simile). Quindi puoi testare GetRootLevels confrontando l'oggetto return con quello che hai impostato sulla tua classe mock. – dcidral

+0

Grazie, farò un tentativo. – Tuco

+0

Per farlo funzionare devo prendere in giro la proprietà Levels che è una DataServiceQuery , che ha solo getter. Sto ancora pensando – Tuco

risposta

7

Il lettore di DataServiceQuery è privato, quindi non è possibile prendere in giro utilizzando Moq.

ho usato MsFakes come uno strumento di codice di tessitura gratuito per risolvere questo problema:

[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

Grazie, lo farò fare un tentativo, ti farò sapere il risultato. – Tuco

+0

Questa dovrebbe essere la risposta, ma sembra che [Ho bisogno di VS premium edition per abilitare MSFakes] (http: //visualstudio.uservoice.com/forums/121579-visual-studio/suggerimenti/2919309-fornire-microsoft-fakes-with-all-visual-studio-edi) e ho l'edizione Professional, cercherò di ottenere una copia di edizione premium e I ' Farò un test, ho svalutato questa risposta – Tuco

+0

@ATM ah Non sapevo questo problema premium ... Puoi aggiungere una proprietà virtuale che esporrà 'IEnumerable ' e poi usarlo nel tuo codice: 'public IEnumerable GetLevels {get {return Levels;}} '. Con questo approccio non hai bisogno di uno strumento per tessere il codice. BTW Ho contrassegnato 'Levels' come metodo virtuale nel mio test ... –