Como eu uso o Moq para zombar de um método de extensão?

Estou escrevendo um teste que depende dos resultados de um método de extensão, mas não quero que uma falha futura desse método de extensão interrompa esse teste. Zombar desse resultado parecia a escolha óbvia, mas o Moq não parece oferecer uma maneira de replace um método estático (um requisito para um método de extensão). Há uma ideia semelhante com Moq.Protected e Moq.Stub, mas eles não parecem oferecer nada para este cenário. Estou faltando alguma coisa ou devo estar fazendo isso de uma maneira diferente?

Aqui está um exemplo trivial que falha com o usual “Expectativa inválida em um membro não substituível” . Este é um mau exemplo da necessidade de zombar de um método de extensão, mas deveria fazê-lo.

public class SomeType { int Id { get; set; } } var ListMock = new Mock<List>(); ListMock.Expect(l => l.FirstOrDefault(st => st.Id == 5)) .Returns(new SomeType { Id = 5 }); 

Quanto a qualquer viciado em TypeMock que possa sugerir que eu use o Isolator: Aprecio o esforço, já que parece que o TypeMock poderia fazer o trabalho de olhos vendados e inebriado, mas nosso orçamento não está aumentando tão cedo.

Os methods de extensão são apenas methods estáticos disfarçados. Estruturas de simulação como o Moq ou o Rhinomocks só podem criar instâncias falsas de objects, o que significa que não é possível zombar de methods estáticos.

Se você controlar a definição dos methods de extensão (ou seja, eles não são os integrados do LINQ), há outra alternativa, explicada em “Tornando os methods de extensão passíveis de zombaria”

Eu sei que esta questão não está ativa há cerca de um ano, mas a Microsoft lançou um framework para lidar exatamente com isso chamado Moles .

Aqui estão alguns tutoriais também:

  • DimeCasts.net
  • Tutorial de Nikolai Tillman
  • Eu criei uma class wrapper para os methods de extensão que eu precisava para zombar.

     public static class MyExtensions { public static string MyExtension(this T obj) { return "Hello World!"; } } public interface IExtensionMethodsWrapper { string MyExtension(T myObj); } public class ExtensionMethodsWrapper : IExtensionMethodsWrapper { public string MyExtension(T myObj) { return myObj.MyExtension(); } } 

    Então você pode zombar dos methods wrapper em seus testes e codificar com seu container IOC.

    Se você puder alterar o código dos methods de extensão, poderá codificá-lo desta forma para poder testar:

     using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; public static class MyExtensions { public static IMyImplementation Implementation = new MyImplementation(); public static string MyMethod(this object obj) { return Implementation.MyMethod(obj); } } public interface IMyImplementation { string MyMethod(object obj); } public class MyImplementation : IMyImplementation { public string MyMethod(object obj) { return "Hello World!"; } } 

    Portanto, os methods de extensão são apenas um wrapper em torno da interface de implementação.

    (Você pode usar apenas a class de implementação sem methods de extensão que sejam meio sintáticos).

    E você pode zombar da interface de implementação e configurá-la como implementação para a class de extensões.

     public class MyClassUsingExtensions { public string ReturnStringForObject(object obj) { return obj.MyMethod(); } } [TestClass] public class MyTests { [TestMethod] public void MyTest() { // Given: //------- var mockMyImplementation = new Mock(); MyExtensions.Implementation = mockMyImplementation.Object; var myObject = new Object(); var myClassUsingExtensions = new MyClassUsingExtensions(); // When: //------- myClassUsingExtensions.ReturnStringForObject(myObject); //Then: //------- // This would fail because you cannot test for the extension method //mockMyImplementation.Verify(m => m.MyMethod()); // This is success because you test for the mocked implementation interface mockMyImplementation.Verify(m => m.MyMethod(myObject)); } } 

    Para methods de extensão, normalmente uso a seguinte abordagem:

     public static class MyExtensions { public static Func _doSumm = (x, y) => x + y; public static int Summ(this int x, int y) { return _doSumm(x, y); } } 

    Permite injetar _doSumm razoavelmente fácil.