Moq - 带有可为空参数的方法评估抛出空异常
Moq - Method evaluation with nullable parameter throws null exception
我知道我可以新建一个对象并将其传入,但我正在尝试匹配语法和模式,因为该值从未实际使用过。
实施:
public interface IDemoProvider
{
string GetStringById(int? id);
}
public interface IDemoService
{
bool Validate(ServiceRequest request);
}
public class ServiceRequest
{
public string foo { get; set; }
public int? bar { get; set; }
}
public class SomeDemoProvider : IDemoProvider
{
public virtual string GetStringById(int? id)
{
if (id != null)
return "success";
else
return "else";
}
}
public class BaseDemoService : IDemoService
{
public IDemoProvider Provider { get; set; }
public virtual bool Validate(ServiceRequest request)
{
if (request.bar == null)
return false;
return true;
}
}
public class SomeDemoService : BaseDemoService
{
public SomeDemoService(IDemoProvider provider)
{
Provider = provider;
}
public virtual string Post(ServiceRequest request)
{
if (!Validate(request))
throw new Exception("Invalid Request");
return Provider.GetStringById(request.bar);
}
}
测试代码:
[TestFixture]
public class ProgramTests
{
[Test]
public void SomeDemoServiceTest()
{
var mockProvider = new Mock<IDemoProvider>();
mockProvider.Setup(p => p.GetStringById(It.IsAny<int?>())).Returns(() => "success");
object[] args = { mockProvider.Object };
var mockService = new Mock<SomeDemoService>(args) {CallBase = true};
mockService.Setup(s => s.Validate(It.IsAny<ServiceRequest>())).Returns(true);
string result = mockService.Object.Post(It.IsAny<ServiceRequest>());
Assert.AreEqual("success", result);
}
}
问题
这导致 NullReferenceException
这是有道理的,因为它试图解析 request.Id
而 运行 在请求级别为空。
但是,如果我用 null
替换参数,它工作正常:
return Provider.GetStringById(null);
我的问题是是否有某种方法可以让拦截器在评估表达式之前识别它应该 return 值而不是尝试评估表达式。
我可以模拟出请求对象的其余部分,但这看起来很愚蠢,因为根据测试从未使用过这些值。这里真正的问题是我是否应该以不同的方式编写代码或测试以避免这种情况,或者我是否使用简单的解决方法使问题复杂化。
你测试错了。
如果您正在尝试执行测试中的方法,那么这一行...
string result = mockService.Object.Post(It.IsAny<ServiceRequest>());
使用不当。您将使用 It.IsAny<ServiceRequest>()
作为设置的一部分。在您尝试使用它的情况下,它将导致 Validate
方法中的 null
request.bar == null
因为 It.IsAny
表达式实际上并没有将任何东西传递给方法。
阅读如何通过 Moq Quickstart 使用 Moq 以更好地了解如何在测试时使用它。
将测试重写为
public void SomeDemoServiceTest() {
//Arrange
var mockProvider = new Mock<IDemoProvider>();
mockProvider.Setup(p => p.GetStringById(It.IsAny<int?>())).Returns(() => "success");
var mockService = new Mock<SomeDemoService>(mockProvider.Object) { CallBase = true };
mockService.Setup(s => s.Validate(It.IsAny<ServiceRequest>())).Returns(true);
var request = new ServiceRequest();
//Act
var result = mockService.Object.Post(request);
//Assert
Assert.AreEqual("success", result);
}
并且测试应该通过。
我知道我可以新建一个对象并将其传入,但我正在尝试匹配语法和模式,因为该值从未实际使用过。
实施:
public interface IDemoProvider
{
string GetStringById(int? id);
}
public interface IDemoService
{
bool Validate(ServiceRequest request);
}
public class ServiceRequest
{
public string foo { get; set; }
public int? bar { get; set; }
}
public class SomeDemoProvider : IDemoProvider
{
public virtual string GetStringById(int? id)
{
if (id != null)
return "success";
else
return "else";
}
}
public class BaseDemoService : IDemoService
{
public IDemoProvider Provider { get; set; }
public virtual bool Validate(ServiceRequest request)
{
if (request.bar == null)
return false;
return true;
}
}
public class SomeDemoService : BaseDemoService
{
public SomeDemoService(IDemoProvider provider)
{
Provider = provider;
}
public virtual string Post(ServiceRequest request)
{
if (!Validate(request))
throw new Exception("Invalid Request");
return Provider.GetStringById(request.bar);
}
}
测试代码:
[TestFixture]
public class ProgramTests
{
[Test]
public void SomeDemoServiceTest()
{
var mockProvider = new Mock<IDemoProvider>();
mockProvider.Setup(p => p.GetStringById(It.IsAny<int?>())).Returns(() => "success");
object[] args = { mockProvider.Object };
var mockService = new Mock<SomeDemoService>(args) {CallBase = true};
mockService.Setup(s => s.Validate(It.IsAny<ServiceRequest>())).Returns(true);
string result = mockService.Object.Post(It.IsAny<ServiceRequest>());
Assert.AreEqual("success", result);
}
}
问题
这导致 NullReferenceException
这是有道理的,因为它试图解析 request.Id
而 运行 在请求级别为空。
但是,如果我用 null
替换参数,它工作正常:
return Provider.GetStringById(null);
我的问题是是否有某种方法可以让拦截器在评估表达式之前识别它应该 return 值而不是尝试评估表达式。
我可以模拟出请求对象的其余部分,但这看起来很愚蠢,因为根据测试从未使用过这些值。这里真正的问题是我是否应该以不同的方式编写代码或测试以避免这种情况,或者我是否使用简单的解决方法使问题复杂化。
你测试错了。
如果您正在尝试执行测试中的方法,那么这一行...
string result = mockService.Object.Post(It.IsAny<ServiceRequest>());
使用不当。您将使用 It.IsAny<ServiceRequest>()
作为设置的一部分。在您尝试使用它的情况下,它将导致 Validate
方法中的 null
request.bar == null
因为 It.IsAny
表达式实际上并没有将任何东西传递给方法。
阅读如何通过 Moq Quickstart 使用 Moq 以更好地了解如何在测试时使用它。
将测试重写为
public void SomeDemoServiceTest() {
//Arrange
var mockProvider = new Mock<IDemoProvider>();
mockProvider.Setup(p => p.GetStringById(It.IsAny<int?>())).Returns(() => "success");
var mockService = new Mock<SomeDemoService>(mockProvider.Object) { CallBase = true };
mockService.Setup(s => s.Validate(It.IsAny<ServiceRequest>())).Returns(true);
var request = new ServiceRequest();
//Act
var result = mockService.Object.Post(request);
//Assert
Assert.AreEqual("success", result);
}
并且测试应该通过。