Pergunta

Como posso zombar um DataServiceQuery para fins de teste de unidade?

Long Detalhes seguir: Imagine um aplicativo ASP.NET MVC, onde as conversações controlador a um ADO.NET DataService que encapsula o armazenamento dos nossos modelos (por exemplo, causa nós estaremos lendo uma lista de clientes). Com uma referência ao serviço, temos uma classe gerada herdando de DataServiceContext:

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 */
  }
}

O controlador poderia ser:

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

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

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

Como você pode ver, eu usei um construtor que aceita uma instância IMyDataContext para que possamos utilizar um mock em nossa unidade de teste:

[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 e MyDataContext necessidade de implementar a mesma interface de IMyDataContext:

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

No entanto, quando tentar implementar a classe MockContext, nos deparamos com problemas devido à natureza de DataServiceQuery (que, para ser claro, estamos usando na interface IMyDataContext simplesmente porque esse é o tipo de dados que encontrámos no auto classe MyDataContext -generated que começou com). Se tentarmos escreve:

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

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

Nos Clientes getter gostaríamos de instanciar uma instância DataServiceQuery, preenchê-lo com os Clientes em CustomersToReturn, e devolvê-lo. Os problemas que eu funciono em:

1 ~ DataServiceQuery não tem nenhum construtor público; Para instanciar um que você deve chamar CreateQuery em um DataServiceContext; consulte MSDN

2 ~ Se eu fizer a herdar MockContext de DataServiceContext bem, e chamar CreateQuery para obter um DataServiceQuery para uso, o serviço ea consulta tem que ser amarrado a uma URI válida e, quando tento para iterar ou acessar os objetos na consulta, ele vai tentar e executar contra esse URI. Em outras palavras, se eu mudar o MockContext como tal:

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

Em seguida, no teste de unidade, teremos um erro na linha marcada como linha A, porque http: // www .contoso.com não hospeda o nosso serviço. O mesmo erro é acionado, mesmo se uma linha tenta fazer com que o número de elementos em modelos. Agradecemos antecipadamente.

Foi útil?

Solução

[Disclaimer - Eu trabalho em Typemock]

Você já pensou em usar um quadro zombando?

Você pode usar Typemock Isolador para criar uma instância falsa de DataServiceQuery:

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

E você pode criar um falso semelhante DataServiceContext e configurá-lo de comportamento em vez de tentar herdá-lo.

Outras dicas

Eu resolvi isso criando um IDataServiceQuery de interface com duas implementações:

  • DataServiceQueryWrapper
  • MockDataServiceQuery

Eu, então, usar IDataServiceQuery onde quer que eu teria usado anteriormente um DataServiceQuery.

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

O DataServiceQueryWrapper leva um DataServiceQuery em sua delegados construtor e, em seguida, toda a funcionalidade para a consulta passado. Da mesma forma, o MockDataServiceQuery leva um tudo IQueryable e delegados que pode para a consulta.

Para os métodos IDataServiceQuery simulados, eu atualmente apenas this voltar, embora você poderia fazer algo para zombar a funcionalidade se você quiser.

Por exemplo:

// (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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top