nUnit / moq - 为什么我的模拟总是 return false?
nUnit / moq - why does my mock always return false?
我刚开始使用 nUnit 和 Moq 进行单元测试,运行 遇到了 dbprovider 中方法的问题设置行为。
我正在尝试测试调用 ICoDbProvider 中的 Exists 方法的验证方法。当为 false 时,该方法会抛出异常,并且运行良好。当为真时,该方法应该继续执行,直到方法末尾的 return true;
语句。
这是测试的方法:
private bool ValidateReciboCajaModel(ReciboCajaModel model)
{
if (model == null)
throw new ArgumentException("El modelo ha llegado nulo");
if (string.IsNullOrWhiteSpace(model.AccionARealizar))
throw new ArgumentException("No se ha definido una Acción a realizar");
if (!_accionARealizarService.Exists(new AccionARealizarEntity(model)))
throw new ArgumentException(@"No es una ""acción a realizar"" válida");
if (string.IsNullOrWhiteSpace(model.CentroCostos))
throw new ArgumentException("No se ha definido ningún centro de costos");
if (!_centroCostosService.Exists(new CentroCostosEntity(model)))
throw new Exception("No es un centro de costos válido");
if (String.IsNullOrWhiteSpace(model.CuentaIngresoDinero))
throw new Exception("No es una cuenta de Ingreso válida");
if (!_terceroService.Exists(new TerceroEntity(model)))
throw new Exception("No es un tercero registrado");
if (String.IsNullOrWhiteSpace(model.Tercero))
throw new Exception("No es un tercero válido");
if (!_tipoReciboCajaService.Exists(new TipoReciboCajaEntity(model)))
throw new Exception("No es un recibo de caja registrado");
if (String.IsNullOrWhiteSpace(model.TipoReciboCaja))
throw new Exception("No es un recibo de caja válido");
if (!(0 < model.ValorPagado && model.ValorPagado <= 999999999999d))
throw new Exception("El valor pagado no es válido");
return true;
}
该测试最初由Intellitest生成,它生成了一个用于私有方法测试的脚手架。
测试方法是这样的
[Test]
[PexGeneratedBy(typeof(ReciboCajaBizTest))]
[PexRaisedException(typeof(TargetInvocationException))]
public void ValidateReciboCajaModel_ValidModel()
{
bool b;
TargetInvocationException receivedException = new TargetInvocationException(null);
ReciboCajaModel s1 = new ReciboCajaModel();
var accionARealizarService = new Mock<ICoDbProvider>();
var centroCostosService = new Mock<ICoDbProvider>();
var terceroService = new Mock<ICoDbProvider>();
var tipoReciboCajaService = new Mock<ICoDbProvider>();
s1.TipoReciboCaja = "RC0";
s1.Numero = 0;
s1.Tercero = "tercero existente";
s1.AccionARealizar = "some action";
s1.FechaElaboracion = default(DateTime);
s1.CentroCostos = "cc1";
s1.CuentaIngresoDinero = "Débito";
s1.ValorPagado = 1000000d;
accionARealizarService.Setup(m => m.Exists(new AccionARealizarEntity(s1))).Returns(true);
centroCostosService.Setup(m => m.Exists(new CentroCostosEntity(s1))).Returns(true);
terceroService.Setup(m => m.Exists(new TerceroEntity(s1))).Returns(true);
tipoReciboCajaService.Setup(m => m.Exists(new TipoReciboCajaEntity(s1))).Returns(true);
ReciboCajaBiz s0 = new ReciboCajaBiz(null, accionARealizarService.Object, centroCostosService.Object,terceroService.Object,tipoReciboCajaService.Object);
b = this.ValidateReciboCajaModel(s0, s1);
Assert.AreEqual(true, b);
}
在这种情况下,模型中的所有内容都是正确的,方法应该 return 一个有效的标志。
我在调试时有这种奇怪的行为。在设置 Mocks 行为后,我在 Watch window 中调用了方法,它 return false,如图
我还有一个测试 accionARealizar 对象不存在的方法,它 return 设置为 false。但我怀疑它是否正常工作。
更新
根据下面评论中的要求,这是 ReciboCajaModel
public class ReciboCajaModel
{
[Required]
[EnumDataType(enumType: typeof(TipoReciboCaja))]
public string TipoReciboCaja { get; set; }
[Required]
public int Numero { get; set; }
[Required]
public string Tercero { get; set; }
[Required]
public string AccionARealizar { get; set; }
[Required]
public DateTime FechaElaboracion { get; set; }
[Required]
public string CentroCostos { get; set; }
/// <summary>
/// este campo no sólo enlaza la cuenta donde se hace el abono o accion a realizar, sino también la forma de pago
/// </summary>
[Required]
public string CuentaIngresoDinero { get; set; }
[Required]
[Range(0, 999999999999)]
public double ValorPagado { get; set; }
}
和 ReciboCajaBiz 构造函数和私有成员
private ICoDbProvider _reciboCajaService;
private ICoDbProvider _accionARealizarService;
private ICoDbProvider _centroCostosService;
private ICoDbProvider _terceroService;
private ICoDbProvider _tipoReciboCajaService;
public ReciboCajaBiz()
{
_reciboCajaService = new ReciboCajaService();
_accionARealizarService = new AccionARealizarService();
}
public ReciboCajaBiz(ICoDbProvider reciboCajaService = null, ICoDbProvider accionARealizarService = null, ICoDbProvider centroCostosService = null, ICoDbProvider terceroService = null, ICoDbProvider tipoReciboCajaService = null)
{
_reciboCajaService = reciboCajaService == null ? new ReciboCajaService() : reciboCajaService;
_accionARealizarService = accionARealizarService == null ? new AccionARealizarService() : accionARealizarService;
_centroCostosService = centroCostosService == null ? new CentroCostosService() : centroCostosService;
_terceroService = terceroService == null ? new TerceroService() : terceroService;
_tipoReciboCajaService = tipoReciboCajaService == null ? new TipoReciboCajaService() : terceroService;
}
乍一看,您的 Moq 表达式可能与您预期的不匹配。
您试图匹配 Exists() 函数的对象引用,这意味着它将对对象引用进行相等性检查 - 但不会匹配。您新建了两个不同的对象。
_accionARealizarService.Exists(new AccionARealizarEntity(model))
See here for more info on Moq Matching Arguments
更改 mock 的 Setup
表达式,以便更慷慨地查看是否是这个问题。
您可以在 like 表达式中使用 It.IsAny()
。
accionARealizarService.Setup(m => m.Exists(It.IsAny<SiigoEntity>())).Returns(true);
一旦成功,您可以使用 It.Is()
表达式获得更具体的信息,以进行对象 属性 检查。
问题是在模拟的行为集中使用了对象引用。我正在使用这个:
accionARealizarService.Setup(m => m.Exists(new AccionARealizarEntity(s1))).Returns(true);
但这会导致测试创建一个对象并将该对象与发送到 Exists 方法的对象进行比较,以便它可以验证行为。这将导致调用 Equals
方法,因此它总是会抛出 false。
为了正确验证发送到模拟方法的参数,我应该使用这个:
accionARealizarService.Setup(m => m.Exists(It.IsAny<CoEntity>())).Returns(true);
It.IsAny<CoEntity>
语句将指示模拟方法行为不与对象的特定实例进行比较,而是验证对象是否为 CoEntity
。
我得出了这个结论,post 这个完整的答案基于@Ralph 的回答,这让我走上了正确的道路。
我刚开始使用 nUnit 和 Moq 进行单元测试,运行 遇到了 dbprovider 中方法的问题设置行为。
我正在尝试测试调用 ICoDbProvider 中的 Exists 方法的验证方法。当为 false 时,该方法会抛出异常,并且运行良好。当为真时,该方法应该继续执行,直到方法末尾的 return true;
语句。
这是测试的方法:
private bool ValidateReciboCajaModel(ReciboCajaModel model)
{
if (model == null)
throw new ArgumentException("El modelo ha llegado nulo");
if (string.IsNullOrWhiteSpace(model.AccionARealizar))
throw new ArgumentException("No se ha definido una Acción a realizar");
if (!_accionARealizarService.Exists(new AccionARealizarEntity(model)))
throw new ArgumentException(@"No es una ""acción a realizar"" válida");
if (string.IsNullOrWhiteSpace(model.CentroCostos))
throw new ArgumentException("No se ha definido ningún centro de costos");
if (!_centroCostosService.Exists(new CentroCostosEntity(model)))
throw new Exception("No es un centro de costos válido");
if (String.IsNullOrWhiteSpace(model.CuentaIngresoDinero))
throw new Exception("No es una cuenta de Ingreso válida");
if (!_terceroService.Exists(new TerceroEntity(model)))
throw new Exception("No es un tercero registrado");
if (String.IsNullOrWhiteSpace(model.Tercero))
throw new Exception("No es un tercero válido");
if (!_tipoReciboCajaService.Exists(new TipoReciboCajaEntity(model)))
throw new Exception("No es un recibo de caja registrado");
if (String.IsNullOrWhiteSpace(model.TipoReciboCaja))
throw new Exception("No es un recibo de caja válido");
if (!(0 < model.ValorPagado && model.ValorPagado <= 999999999999d))
throw new Exception("El valor pagado no es válido");
return true;
}
该测试最初由Intellitest生成,它生成了一个用于私有方法测试的脚手架。
测试方法是这样的
[Test]
[PexGeneratedBy(typeof(ReciboCajaBizTest))]
[PexRaisedException(typeof(TargetInvocationException))]
public void ValidateReciboCajaModel_ValidModel()
{
bool b;
TargetInvocationException receivedException = new TargetInvocationException(null);
ReciboCajaModel s1 = new ReciboCajaModel();
var accionARealizarService = new Mock<ICoDbProvider>();
var centroCostosService = new Mock<ICoDbProvider>();
var terceroService = new Mock<ICoDbProvider>();
var tipoReciboCajaService = new Mock<ICoDbProvider>();
s1.TipoReciboCaja = "RC0";
s1.Numero = 0;
s1.Tercero = "tercero existente";
s1.AccionARealizar = "some action";
s1.FechaElaboracion = default(DateTime);
s1.CentroCostos = "cc1";
s1.CuentaIngresoDinero = "Débito";
s1.ValorPagado = 1000000d;
accionARealizarService.Setup(m => m.Exists(new AccionARealizarEntity(s1))).Returns(true);
centroCostosService.Setup(m => m.Exists(new CentroCostosEntity(s1))).Returns(true);
terceroService.Setup(m => m.Exists(new TerceroEntity(s1))).Returns(true);
tipoReciboCajaService.Setup(m => m.Exists(new TipoReciboCajaEntity(s1))).Returns(true);
ReciboCajaBiz s0 = new ReciboCajaBiz(null, accionARealizarService.Object, centroCostosService.Object,terceroService.Object,tipoReciboCajaService.Object);
b = this.ValidateReciboCajaModel(s0, s1);
Assert.AreEqual(true, b);
}
在这种情况下,模型中的所有内容都是正确的,方法应该 return 一个有效的标志。
我在调试时有这种奇怪的行为。在设置 Mocks 行为后,我在 Watch window 中调用了方法,它 return false,如图
我还有一个测试 accionARealizar 对象不存在的方法,它 return 设置为 false。但我怀疑它是否正常工作。
更新
根据下面评论中的要求,这是 ReciboCajaModel
public class ReciboCajaModel
{
[Required]
[EnumDataType(enumType: typeof(TipoReciboCaja))]
public string TipoReciboCaja { get; set; }
[Required]
public int Numero { get; set; }
[Required]
public string Tercero { get; set; }
[Required]
public string AccionARealizar { get; set; }
[Required]
public DateTime FechaElaboracion { get; set; }
[Required]
public string CentroCostos { get; set; }
/// <summary>
/// este campo no sólo enlaza la cuenta donde se hace el abono o accion a realizar, sino también la forma de pago
/// </summary>
[Required]
public string CuentaIngresoDinero { get; set; }
[Required]
[Range(0, 999999999999)]
public double ValorPagado { get; set; }
}
和 ReciboCajaBiz 构造函数和私有成员
private ICoDbProvider _reciboCajaService;
private ICoDbProvider _accionARealizarService;
private ICoDbProvider _centroCostosService;
private ICoDbProvider _terceroService;
private ICoDbProvider _tipoReciboCajaService;
public ReciboCajaBiz()
{
_reciboCajaService = new ReciboCajaService();
_accionARealizarService = new AccionARealizarService();
}
public ReciboCajaBiz(ICoDbProvider reciboCajaService = null, ICoDbProvider accionARealizarService = null, ICoDbProvider centroCostosService = null, ICoDbProvider terceroService = null, ICoDbProvider tipoReciboCajaService = null)
{
_reciboCajaService = reciboCajaService == null ? new ReciboCajaService() : reciboCajaService;
_accionARealizarService = accionARealizarService == null ? new AccionARealizarService() : accionARealizarService;
_centroCostosService = centroCostosService == null ? new CentroCostosService() : centroCostosService;
_terceroService = terceroService == null ? new TerceroService() : terceroService;
_tipoReciboCajaService = tipoReciboCajaService == null ? new TipoReciboCajaService() : terceroService;
}
乍一看,您的 Moq 表达式可能与您预期的不匹配。
您试图匹配 Exists() 函数的对象引用,这意味着它将对对象引用进行相等性检查 - 但不会匹配。您新建了两个不同的对象。
_accionARealizarService.Exists(new AccionARealizarEntity(model))
See here for more info on Moq Matching Arguments
更改 mock 的 Setup
表达式,以便更慷慨地查看是否是这个问题。
您可以在 like 表达式中使用 It.IsAny()
。
accionARealizarService.Setup(m => m.Exists(It.IsAny<SiigoEntity>())).Returns(true);
一旦成功,您可以使用 It.Is()
表达式获得更具体的信息,以进行对象 属性 检查。
问题是在模拟的行为集中使用了对象引用。我正在使用这个:
accionARealizarService.Setup(m => m.Exists(new AccionARealizarEntity(s1))).Returns(true);
但这会导致测试创建一个对象并将该对象与发送到 Exists 方法的对象进行比较,以便它可以验证行为。这将导致调用 Equals
方法,因此它总是会抛出 false。
为了正确验证发送到模拟方法的参数,我应该使用这个:
accionARealizarService.Setup(m => m.Exists(It.IsAny<CoEntity>())).Returns(true);
It.IsAny<CoEntity>
语句将指示模拟方法行为不与对象的特定实例进行比较,而是验证对象是否为 CoEntity
。
我得出了这个结论,post 这个完整的答案基于@Ralph 的回答,这让我走上了正确的道路。