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 Moq não parece oferecer uma maneira de substituir um método estático (um requisito para um método de extensão). Existe uma ideia semelhante com Moq.Protected e Moq.Stub, mas eles não parecem oferecer nada para este cenário. Estou perdendo alguma coisa ou devo fazer isso de uma maneira diferente?
Aqui está um exemplo trivial que falha com a usual "Expectativa inválida em um membro não substituível" . Este é um mau exemplo de necessidade de simular um método de extensão, mas deve servir.
public class SomeType {
int Id { get; set; }
}
var ListMock = new Mock<List<SomeType>>();
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 em vez disso: agradeço o esforço, pois parece que o TypeMock poderia fazer o trabalho vendado e embriagado, mas nosso orçamento não aumentará tão cedo.
fonte
Respostas:
Os métodos de extensão são apenas métodos estáticos disfarçados. Mocking frameworks como Moq ou Rhinomocks só podem criar instâncias mock de objetos, isso significa que mocking métodos estáticos não é possível.
fonte
Se você pode alterar o código dos métodos de extensão, você pode codificá-lo assim 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 métodos de extensão são apenas um invólucro em torno da interface de implementação.
(Você poderia usar apenas a classe de implementação sem métodos de extensão que são uma espécie de açúcar sintático.)
E você pode simular a interface de implementação e defini-la como implementação para a classe 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<IMyImplementation>(); MyExtensions.Implementation = mockMyImplementation.Object; var myClassUsingExtensions = new MyClassUsingExtensions(); // When: //------- var myObject = new Object(); 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)); } }
fonte
Eu sei que esta pergunta não está ativa há cerca de um ano, mas a Microsoft lançou uma estrutura para lidar exatamente com isso, chamado Moles .
Aqui estão alguns tutoriais também:
fonte
Eu criei uma classe wrapper para os métodos de extensão que eu precisava simular.
public static class MyExtensions { public static string MyExtension<T>(this T obj) { return "Hello World!"; } } public interface IExtensionMethodsWrapper { string MyExtension<T>(T myObj); } public class ExtensionMethodsWrapper : IExtensionMethodsWrapper { public string MyExtension<T>(T myObj) { return myObj.MyExtension(); } }
Em seguida, você pode simular os métodos de wrapper em seus testes e código com seu contêiner IOC.
fonte
Para métodos de extensão, normalmente uso a seguinte abordagem:
public static class MyExtensions { public static Func<int,int, int> _doSumm = (x, y) => x + y; public static int Summ(this int x, int y) { return _doSumm(x, y); } }
Ele permite injetar _doSumm com bastante facilidade.
fonte
A melhor coisa que você pode fazer é fornecer uma implementação personalizada para o tipo que tem o método de extensão, por exemplo:
[Fact] public class Tests { public void ShouldRunOk() { var service = new MyService(new FakeWebHostEnvironment()); // Service.DoStuff() internally calls the SomeExtensionFunction() on IWebHostEnvironment // Here it works just fine as we provide a custom implementation of that interface service.DoStuff().Should().NotBeNull(); } } public class FakeWebHostEnvironment : IWebHostEnvironment { /* IWebHostEnvironment implementation */ public bool SomeExtensionFunction() { return false; } }
fonte