2015-12-04 12 views
8

Ich verwende Web Api OData v4 auf dem Server und OData Client code generator auf dem Client. Es funktioniert gut, aber ich kann den Code auf dem Client nicht testen.Wie wird der OData Client getestet?

Auf dem Server expose ich ein "Levels" dbSet.

Hier ist ein Snippet-Code auf dem 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(); 
    } 
} 

Ich bin der OData Behälter durch die T4-Vorlage als Parameter für den Konstruktor, um es in der Lage sein zu akzeptieren erzeugt irgendwie zu verspotten.

Unit-Test, hier ist, wo ich bin verloren:

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

Also in diesem Gerät zu testen Ich möchte nur die Logik innerhalb der GetRootLevels Methode testen, ich möchte nicht einen Integrationstest oder ein machen Self-Hosting-Service, ich möchte nur die Methode mit In-Memory-Daten testen.

Wie verspotten Sie die vom OData-Client generierte Klasse, die eigentlich eine DataServiceContext-Klasse ist?

Ich verwende Moq, aber es kann

bearbeiten alles, (in VS Professional Edition frei oder zumindest im Lieferumfang enthalten) sein: Hier ist die Implementierung von ODataContainer (Sie erinnern sich dies durch OData-Client automatisch generiert wird)

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

Sie könnten sicherstellen, nie eine Null-DefaultContainer Testen der Klassen erhalten, die diesen Konstruktor verwenden. Ändern Sie dann den Parameter, um eine Schnittstelle zu verwenden (IDefaultContainer oder etwas Ähnliches). Dann können Sie GetRootLevels testen, indem Sie das Rückgabeobjekt mit dem vergleichen, was Sie in Ihrer Mock-Klasse eingerichtet haben. – dcidral

+0

Danke, ich werde es versuchen. – Tuco

+0

Dazu muss ich die Levels-Eigenschaft, die eine DataServiceQuery ist , die nur Getter hat. Ich denke immer noch – Tuco

Antwort

7

Der C'tor von DataServiceQuery ist privat, daher konnte ich es nicht mit Moq spotten.

benutzte ich MsFakes als Werkzeug frei Code Weben dieses Problem zu lösen:

[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

Danke, ich werde Probieren Sie es aus, ich lasse Sie das Ergebnis wissen. – Tuco

+0

Dies sollte die Antwort sein, aber es scheint, dass [ich brauche VS Premium-Edition MSFakes aktivieren] (http: //visualstudio.uservoice.com/forums/121579-Visual-Studio/Vorschläge/2919309-bieten-Microsoft-Fälschungen-mit-allen-Visual-Studio-edi) und ich habe Professional Edition, ich werde versuchen, eine Kopie der Premium-Edition zu bekommen und ich ' ll machen einen Test, ich habe diese Antwort upovoted – Tuco

+0

@ATM ah Ich wusste nicht, dieses Premium-Problem ... Sie können eine virtuelle Eigenschaft hinzufügen, die 'IEnumerable ' dann verwenden Sie es in Ihrem Code: 'öffentliche virtuelle IEnumerable GetLevels {get {return Levels;}} '. Mit diesem Ansatz benötigen Sie kein Code-Web-Tool. BTW Ich markierte 'Levels' als virtuelle Methode in meinem Test ... –

Verwandte Themen