如何 Moq Fluent 接口/链方法
how to Moq Fluent interface / chain methods
我正在使用 Daniel Cazzulino 的 moq 框架,kzu 版本 4.10.1。
我想要最小起订量,这样我就可以测试功能的特定部分(下面是我可以提取的代码的简化版本)
fluent/chain 方法的设计目的是让您可以通过 Id 获取对象并在需要时包含任何其他信息。
当函数调用 moq'ed 方法时,我在获取正确的对象时遇到了一些问题,该方法当前返回的最后一个 moq'ed 对象是错误的
/*My current Moq setup*/
class Program
{
static void Main(string[] args)
{
var mock = new Mock<IFluent>();
var c1 = new ClassA() { Id = 1, Records = new List<int>() { 5, 2, 1, 10 }, MetaData = new List<string>() };
var c2 = new ClassA() { Id = 2, Records = new List<int>(), MetaData = new List<string>() { "X", "Y", "Z" } };
mock.Setup(x => x.GetById(1).IncludeRecords().IncludeMetaData().Get()).Returns (c1);
mock.Setup(x => x.GetById(2).IncludeRecords().IncludeMetaData().Get()).Returns(c2);
var result = new ComputeClass().ComputeStuff(mock.Object);
Console.WriteLine(result);
Console.ReadLine();
}
}
/*Fluent interface and object returned*/
public interface IFluent
{
IFluent GetById(int id);
IFluent IncludeRecords();
IFluent IncludeMetaData();
ClassA Get();
}
public class ClassA
{
public int Id { get; set; }
public ICollection<int> Records { get; set; }
public ICollection<string> MetaData { get; set; }
}
/*the method which is doing the work*/
public class ComputeClass
{
public string ComputeStuff(IFluent fluent)
{
var ids = new List<int>() { 1, 2 };
var result = new StringBuilder();
foreach (var id in ids)
{
var resClass = fluent.GetById(id).IncludeRecords().IncludeMetaData().Get();
result.Append($"Id : {id}, Records: {resClass.Records.Count}, MetaData: {resClass.MetaData.Count}{Environment.NewLine}");
}
return result.ToString();
}
}
Current incorrect result
/*Id : 1, Records: 0, MetaData: 3
Id : 2, Records: 0, MetaData: 3*/
Expected Result
/*Id : 1, Records: 3, MetaData: 0
Id : 2, Records: 0, MetaData: 3*/
最简单的方法是拆分每个设置:
var mock = new Mock<IFluent>();
var mock1 = new Mock<IFluent>();
var mock2 = new Mock<IFluent>();
mock.Setup(x => x.GetById(1)).Returns(mock1.Object);
mock1.Setup(x => x.IncludeRecords()).Returns(mock1.Object);
mock1.Setup(x => x.IncludeMetaData()).Returns(mock1.Object);
mock1.Setup(x => x.Get()).Returns(c1);
mock.Setup(x => x.GetById(2)).Returns(mock2.Object);
mock2.Setup(x => x.IncludeRecords()).Returns(mock2.Object);
mock2.Setup(x => x.IncludeMetaData()).Returns(mock2.Object);
mock2.Setup(x => x.Get()).Returns(c2);
var result = new ComputeClass().ComputeStuff(mock.Object);
如果你想要更复杂的东西,你可以创建一个 extension/utility 来为你处理这一切,看看这个博客 post:https://www.codemunki.es/2014/11/20/mocking-a-fluent-interface-automatically-in-moq/
只是对现有答案的补充。对于 mocking fluent API moq
中有一个有用的选项,它是 SetReturnsDefault
,它可以节省一些 mocking,特别是如果你有大量的 fluent API,例如
var mock = new Mock<IFluent>();
var mock1 = new Mock<IFluent>();
var mock2 = new Mock<IFluent>();
mock.Setup(x => x.GetById(1)).Returns(mock1.Object);
mock1.SetReturnsDefault(mock1.Object);
mock1.Setup(x => x.Get()).Returns(a);
mock.Setup(x => x.GetById(2)).Returns(mock2.Object);
mock2.SetReturnsDefault(mock2.Object);
mock2.Setup(x => x.Get()).Returns(b);
var aa = mock.Object.IncludeMetaData().GetById(1).IncludeMetaData().Get();
var bb = mock.Object.IncludeMetaData.GetById(2).IncludeMetaData.Get();
使用这种方法,您实际上只需要模拟与 fluent API 不同的方法,而不是所有方法。
我正在使用 Daniel Cazzulino 的 moq 框架,kzu 版本 4.10.1。 我想要最小起订量,这样我就可以测试功能的特定部分(下面是我可以提取的代码的简化版本)
fluent/chain 方法的设计目的是让您可以通过 Id 获取对象并在需要时包含任何其他信息。
当函数调用 moq'ed 方法时,我在获取正确的对象时遇到了一些问题,该方法当前返回的最后一个 moq'ed 对象是错误的
/*My current Moq setup*/
class Program
{
static void Main(string[] args)
{
var mock = new Mock<IFluent>();
var c1 = new ClassA() { Id = 1, Records = new List<int>() { 5, 2, 1, 10 }, MetaData = new List<string>() };
var c2 = new ClassA() { Id = 2, Records = new List<int>(), MetaData = new List<string>() { "X", "Y", "Z" } };
mock.Setup(x => x.GetById(1).IncludeRecords().IncludeMetaData().Get()).Returns (c1);
mock.Setup(x => x.GetById(2).IncludeRecords().IncludeMetaData().Get()).Returns(c2);
var result = new ComputeClass().ComputeStuff(mock.Object);
Console.WriteLine(result);
Console.ReadLine();
}
}
/*Fluent interface and object returned*/
public interface IFluent
{
IFluent GetById(int id);
IFluent IncludeRecords();
IFluent IncludeMetaData();
ClassA Get();
}
public class ClassA
{
public int Id { get; set; }
public ICollection<int> Records { get; set; }
public ICollection<string> MetaData { get; set; }
}
/*the method which is doing the work*/
public class ComputeClass
{
public string ComputeStuff(IFluent fluent)
{
var ids = new List<int>() { 1, 2 };
var result = new StringBuilder();
foreach (var id in ids)
{
var resClass = fluent.GetById(id).IncludeRecords().IncludeMetaData().Get();
result.Append($"Id : {id}, Records: {resClass.Records.Count}, MetaData: {resClass.MetaData.Count}{Environment.NewLine}");
}
return result.ToString();
}
}
Current incorrect result
/*Id : 1, Records: 0, MetaData: 3
Id : 2, Records: 0, MetaData: 3*/
Expected Result
/*Id : 1, Records: 3, MetaData: 0
Id : 2, Records: 0, MetaData: 3*/
最简单的方法是拆分每个设置:
var mock = new Mock<IFluent>();
var mock1 = new Mock<IFluent>();
var mock2 = new Mock<IFluent>();
mock.Setup(x => x.GetById(1)).Returns(mock1.Object);
mock1.Setup(x => x.IncludeRecords()).Returns(mock1.Object);
mock1.Setup(x => x.IncludeMetaData()).Returns(mock1.Object);
mock1.Setup(x => x.Get()).Returns(c1);
mock.Setup(x => x.GetById(2)).Returns(mock2.Object);
mock2.Setup(x => x.IncludeRecords()).Returns(mock2.Object);
mock2.Setup(x => x.IncludeMetaData()).Returns(mock2.Object);
mock2.Setup(x => x.Get()).Returns(c2);
var result = new ComputeClass().ComputeStuff(mock.Object);
如果你想要更复杂的东西,你可以创建一个 extension/utility 来为你处理这一切,看看这个博客 post:https://www.codemunki.es/2014/11/20/mocking-a-fluent-interface-automatically-in-moq/
只是对现有答案的补充。对于 mocking fluent API moq
中有一个有用的选项,它是 SetReturnsDefault
,它可以节省一些 mocking,特别是如果你有大量的 fluent API,例如
var mock = new Mock<IFluent>();
var mock1 = new Mock<IFluent>();
var mock2 = new Mock<IFluent>();
mock.Setup(x => x.GetById(1)).Returns(mock1.Object);
mock1.SetReturnsDefault(mock1.Object);
mock1.Setup(x => x.Get()).Returns(a);
mock.Setup(x => x.GetById(2)).Returns(mock2.Object);
mock2.SetReturnsDefault(mock2.Object);
mock2.Setup(x => x.Get()).Returns(b);
var aa = mock.Object.IncludeMetaData().GetById(1).IncludeMetaData().Get();
var bb = mock.Object.IncludeMetaData.GetById(2).IncludeMetaData.Get();
使用这种方法,您实际上只需要模拟与 fluent API 不同的方法,而不是所有方法。