C# 如何使用Moq模拟扩展方法?
我正在编写一个依赖于扩展方法结果的测试,但我不希望该扩展方法将来的失败会破坏这个测试。模拟这个结果似乎是一个显而易见的选择,但Moq似乎并没有提供一种覆盖静态方法的方法(对扩展方法的要求)。Moq.Protected和Moq.Stub也有类似的想法,但它们似乎没有为这种场景提供任何东西。我是错过了什么,还是应该换一种方式 下面是一个简单的例子,它失败于通常的“对不可重写成员的无效期望”。这是一个需要模拟扩展方法的坏例子,但它应该这样做C# 如何使用Moq模拟扩展方法?,c#,mocking,moq,extension-methods,C#,Mocking,Moq,Extension Methods,我正在编写一个依赖于扩展方法结果的测试,但我不希望该扩展方法将来的失败会破坏这个测试。模拟这个结果似乎是一个显而易见的选择,但Moq似乎并没有提供一种覆盖静态方法的方法(对扩展方法的要求)。Moq.Protected和Moq.Stub也有类似的想法,但它们似乎没有为这种场景提供任何东西。我是错过了什么,还是应该换一种方式 下面是一个简单的例子,它失败于通常的“对不可重写成员的无效期望”。这是一个需要模拟扩展方法的坏例子,但它应该这样做 public class SomeType { in
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 });
公共类SomeType{
int Id{get;set;}
}
var ListMock=new Mock();
Expect(l=>l.FirstOrDefault(st=>st.Id==5))
.Returns(新的SomeType{Id=5});
对于那些TypeMock的瘾君子,他们可能会建议我改用隔离器:我很感谢他们的努力,因为看起来TypeMock可以蒙住眼睛,醉醺醺地完成这项工作,但我们的预算不会很快增加。扩展方法只是变相的静态方法。Moq或rhinomock等模拟框架只能创建对象的模拟实例,这意味着模拟静态方法是不可能的。扩展方法只是伪装的静态方法。Moq或rhinomock等模拟框架只能创建对象的模拟实例,这意味着模拟静态方法是不可能的。我知道这个问题已经有一年没有出现了,但微软发布了一个框架来处理这个问题 这里还有一些教程:
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();
}
}
公共静态类MyExtensions
{
公共静态字符串MyExtension(此T对象)
{
返回“你好,世界!”;
}
}
公共接口IExtensionMethodsRapper
{
字符串MyExtension(T myObj);
}
公共类ExtensionMethodsWrapper:IExtensionMethodsWrapper
{
公共字符串MyExtension(T myObj)
{
返回myObj.MyExtension();
}
}
然后,您可以在测试中模拟包装器方法,并使用IOC容器编写代码。我为需要模拟的扩展方法创建了一个包装器类
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();
}
}
公共静态类MyExtensions
{
公共静态字符串MyExtension(此T对象)
{
返回“你好,世界!”;
}
}
公共接口IExtensionMethodsRapper
{
字符串MyExtension(T myObj);
}
公共类ExtensionMethodsWrapper:IExtensionMethodsWrapper
{
公共字符串MyExtension(T myObj)
{
返回myObj.MyExtension();
}
}
然后,您可以在测试中模拟包装器方法,并使用IOC容器编写代码。如果您可以更改扩展方法代码,则可以这样编写,以便能够进行测试:
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!";
}
}
因此,扩展方法只是实现接口的包装器
(您可以只使用实现类,而不使用扩展方法,扩展方法是一种语法糖。)
您可以模拟实现接口并将其设置为extensions类的实现
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));
}
}
公共类MyClassUsingExtensions
{
公共字符串ReturnStringForObject(对象obj)
{
返回obj.MyMethod();
}
}
[测试类]
公共类MyTests
{
[测试方法]
公共无效MyTest()
{
//鉴于:
//-------
var mockMyImplementation=new Mock();
MyExtensions.Implementation=mockMyImplementation.Object;
var myClassUsingExtensions=新的myClassUsingExtensions();
//当:
//-------
var myObject=新对象();
myClassUsingExtensions.ReturnStringForObject(myObject);
//然后:
//-------
//这将失败,因为您无法测试扩展方法
//验证(m=>m.MyMethod());
//这是成功的,因为您测试了模拟的实现接口
验证(m=>m.MyMethod(myObject));
}
}
如果您可以更改扩展方法代码,那么您可以这样编码,以便能够测试:
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!";
}
}
因此,扩展方法只是实现接口的包装器
(您可以只使用实现类,而不使用扩展方法,扩展方法是一种语法糖。)
您可以模拟实现接口并将其设置为extensions类的实现
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));
}
}
公共类MyClassUsingExtensions
{
公共字符串ReturnStringForObject(对象obj)
{
返回obj.MyMethod();
}
}
[测试类]
公共类MyTests
{
[测试方法]
公共无效MyTest()
{
//鉴于:
//-------
var mockMyImplementation=new Mock();
MyExtensions.Implementation=mockMyImplementation.Object;
var myClassUsingExtensions=新的myClassUsingExtensions();
//当:
//-------
var myObject=新对象();
myClassUsingExtensions.ReturnStringForObject(myObject);
//然后:
//-------
//这将失败,因为您无法测试扩展方法
//验证(m=>m.MyMethod());
//这是成功的,因为您测试了模拟的实现接口
验证(m=>m.MyMethod(myObject));
}
}
对于扩展方法,我通常使用以下方法:
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);
}
}
公共静态类MyExtensions
{
公共静态函数(x,y)=>x+y;
公共静态整数总和(此整数x,整数y)
{
返回_doSumm(x,y);
}
}
它允许相当容易地注入doSumm。用于扩展