Cómo burlarse de métodos no virtuales?

[TestMethod] public void TestMethod1() { var mock = new Mock(); mock.Setup(x => x.SendEmail()).Returns(true); var cus = new Customer(); var result = cus.AddCustomer(mock.Object); Assert.IsTrue(result); } public class Customer { public bool AddCustomer(EmailService emailService) { emailService.SendEmail(); Debug.WriteLine("new customer added"); return true; } } public class EmailService { public virtual bool SendEmail() { throw new Exception("send email failed cuz bla bla bla"); } } 

El método EmailService.SendEmail debe ser virtual para EmailService.SendEmail . ¿Hay alguna forma de burlarse de los métodos no virtuales?

Moq no puede burlarse de los métodos no virtuales en las clases. Utilice otros marcos de burla como el aislador tipo simulacro que realmente teje IL en su conjunto o coloque una interfaz en EmailService y EmailService eso.

Burlarse de métodos no virtuales implica el uso de API de perfil de bajo nivel. Por el momento, creo que las únicas opciones disponibles son:

  • TypeMock
  • JustMock

ambos son comerciales, incluso si JustMock tiene una edición lite, métodos virtuales no burladores están disponibles solo con la versión comercial. Como se señala en los comentarios, hay algo de investigación de Microsoft, en el proyecto Pex y Moles

La alternativa a tener que usar métodos virtuales para burlarse es usar interfaces. De esta forma puedes burlar una dependencia completa.

 public interface IEmailService { bool SendEmail(); // etc... } public class EmailService : IEmailService { //... } 

Ahora puede crear IEmailService de la interfaz IEmailService para permitirle burlarse de cualquiera de sus métodos. Por supuesto, deberá cambiar los tipos de variables que contienen objetos IEmailService a IEmailService corresponda.

Como @aqwert y @Felice escribieron al usar Typemock Isolator , es posible (y bastante fácil) simular métodos no virtuales sin agregar o cambiar ningún código, por ejemplo:

 [TestMethod,Isolated] public void TestMethod1() { var mock = Isolate.Fake.Instance(); Isolate.WhenCalled(() => mock.SendEmail()).WillReturn(true); var cust = new Customer(); var result = cust.AddCustomer(mock); Assert.IsTrue(result); } 

como puede ver, la prueba que he creado es similar a la prueba que intentó crear.

Usa pose Le permite reemplazar cualquier método, incluso estático o no virtual. Proyecto bastante nuevo, pero con licencia MIT de fuente completamente abierta. https://github.com/tonerdo/pose

La única forma de simular métodos no virtuales es simular la interfaz utilizada para implementar esa clase con métodos no virtuales. A continuación está el ejemplo.

 public interface IEmployee { DateTime GetDateofJoining(int id); } public class Employee { public DateTime GetDateofJoining(int id) { return DateTime.Now; } } public class Program { static void Main(string[] args) { var employee = new Mock(); employee.Setup(x => x.GetDateofJoining(It.IsAny())).Returns((int x) => DateTime.Now); Console.WriteLine(employee.Object.GetDateofJoining(1)); Console.ReadLine(); } }