Frage

Wie kann ich eine Dataservicequery für Unit-Tests Zweck verspotten?

Long-Details folgen: Stellen Sie sich eine ASP.NET MVC-Anwendung, wo die Steuerung spricht mit einem ADO.NET Dataservice, das die Speicherung unserer Modelle kapselt (zum Beispiel aus Gründen werden wir eine Liste der Kunden liest). Mit einem Verweis auf den Dienst, erhalten wir eine generierte Klasse von Dataservicecontext erben:

namespace Sample.Services
{
  public partial class MyDataContext : global::System.Data.Services.Client.DataServiceContext
  {
    public MyDataContext(global::System.Uri serviceRoot) : base(serviceRoot) { /* ... */ }

    public global::System.Data.Services.Client.DataServiceQuery<Customer> Customers
    {
      get
      {
        if((this._Customers==null))
        {
          this._Customers = base.CreateQuery<Customer>("Customers");
        }
        return this._Customers;
      }
    }
    /* and many more members */
  }
}

Der Regler könnte sein:

namespace Sample.Controllers
{
  public class CustomerController : Controller
  {
    private IMyDataContext context;

    public CustomerController(IMyDataContext context)
    {
      this.context=context;
    }

    public ActionResult Index() { return View(context.Customers); }
  }
}

Wie Sie sehen können, habe ich einen Konstruktor, der eine IMyDataContext Instanz akzeptiert, so dass wir ein Modell in unserem Unit-Test verwenden können:

[TestFixture]
public class TestCustomerController
{
  [Test]
  public void Test_Index()
  {
    MockContext mockContext = new MockContext();
    CustomerController controller = new CustomerController(mockContext);

    var customersToReturn = new List<Customer>
    {
      new Customer{ Id=1, Name="Fred" },
      new Customer{ Id=2, Name="Wilma" }
    };
    mockContext.CustomersToReturn = customersToReturn;

    var result = controller.Index() as ViewResult;

    var models = result.ViewData.Model;

    //Now we have to compare the Customers in models with those in customersToReturn,
    //Maybe by loopping over them?
    foreach(Customer c in models) //*** LINE A ***
    {
      //TODO: compare with the Customer in the same position from customersToreturn
    }
  }
}

MockContext und MyDataContext müssen die gleiche Schnittstelle IMyDataContext implementieren:

namespace Sample.Services
{
  public interface IMyDataContext
  {
    DataServiceQuery<Customer> Customers { get; }
    /* and more */
  }
}

Wenn wir jedoch die MockContext Klasse versuchen und implementieren, führen wir in Probleme aufgrund der Art von Dataservicequery (was klar zu sein, wir sind einfach in der IMyDataContext Schnittstelle verwenden, da, dass der Datentyp ist, die wir in der Auto gefunden -Generated MyDataContext Klasse, die wir begannen mit). Wenn wir versuchen, zu schreiben:

public class MockContext : IMyDataContext
{
  public IList<Customer> CustomersToReturn { set; private get; }

  public DataServiceQuery<Customer> Customers { get { /* ??? */ } }
}

Bei den Kunden Getter wir möchten eine Dataservicequery-Instanz instanziiert, füllen Sie es mit den Kunden in CustomersToReturn, und es zurückgeben. Die Probleme, die ich laufen in:

1 ~ Dataservicequery hat keinen öffentlichen Konstruktor; instanziiert man Sie Create auf einem Dataservicecontext nennen sollte; finden Sie unter MSDN

2 ~ Wenn ich die MockContext von Dataservicecontext erben machen als auch, und rufen Sie Create eine Dataservicequery bekommen zu verwenden, der Service und die Abfrage auf einen gültigen URI werden gebunden haben und, wenn ich versuche, iterieren oder die Objekte in der Abfrage zuzugreifen, wird es versuchen, und führen gegen diese URI. Mit anderen Worten, wenn ich die MockContext als solche zu ändern:

namespace Sample.Tests.Controllers.Mocks
{
  public class MockContext : DataServiceContext, IMyDataContext
  {
    public MockContext() :base(new Uri("http://www.contoso.com")) { }

    public IList<Customer> CustomersToReturn { set; private get; }

    public DataServiceQuery<Customer> Customers
    {
      get
      {
        var query = CreateQuery<Customer>("Customers");
        query.Concat(CustomersToReturn.AsEnumerable<Customer>());
        return query;
      }
    }
  }
}

Dann wird in dem Unit-Test, erhalten wir einen Fehler auf der Linie als LINE A markiert, weil http: // www .contoso.com nicht unsere Service-Host. Der gleiche Fehler wird ausgelöst, auch wenn LINE A versucht, die Anzahl der Elemente in Modellen zu bekommen. Vielen Dank im Voraus.

War es hilfreich?

Lösung

[Disclaimer - ich Typemock arbeiten]

Haben Sie darüber nachgedacht, einen Mockframework mit?

Sie können Typemock Trenner verwenden, um eine gefälschte Instanz von Dataservicequery zu erstellen:

var fake = Isolate.Fake.Instance<DataServiceQuery>();

Und Sie können eine ähnliche gefälschte Dataservicecontext erstellen und es Verhalten ist, anstatt zu versuchen, es zu übernehmen.

Andere Tipps

Ich löste dies durch eine Schnittstelle IDataServiceQuery mit zwei Implementierungen zu erstellen:

  • DataServiceQueryWrapper
  • MockDataServiceQuery

ich dann IDataServiceQuery verwenden, wo immer ich vorher ein DataServiceQuery verwendet hätte.

public interface IDataServiceQuery<TElement> : IQueryable<TElement>, IEnumerable<TElement>, IQueryable, IEnumerable
{
    IDataServiceQuery<TElement> Expand(string path);

    IDataServiceQuery<TElement> IncludeTotalCount();

    IDataServiceQuery<TElement> AddQueryOption(string name, object value);
}

Die DataServiceQueryWrapper nimmt einen DataServiceQuery in seinen Konstruktor und dann die Delegierten alle Funktionen auf die Abfrage übergeben. Auch die MockDataServiceQuery eine IQueryable und Delegierten alles dauert es, um die Abfrage kann.

Für die Mock IDataServiceQuery Methoden, ich zur Zeit nur this zurückkehren, wenn Sie etwas tun könnten, um die Funktionalität zu verspotten, wenn Sie wollen.

Zum Beispiel:

// (in DataServiceQueryWrapper.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
    return new DataServiceQueryWrapper<TElement>(_query.Expand(path));
}

// (in MockDataServiceQuery.cs)
public IDataServiceQuery<TElement> Expand(string path)
{
    return this;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top