无法将最小起订量设置设置为不 return null
Can't get Moq Setup to not return null
我正在尝试设置对我拥有的 IModelFactory 接口的模拟调用。
这里是 IModelFactory 接口
public interface IModelFactory
{
MaterialAcceptedModel Create(MaterialAccepted model);
MaterialAccepted Parse(MaterialAcceptedModel model);
}
这是实现了 IModelFactor 接口的 ModelFactory class (我在这里只添加了我要测试的方法,不需要添加 Create 方法的实现)
public class ModelFactory : IModelFactory
{
private UrlHelper _urlHelper;
private IRTWRepository _repo;
//private IKeysGeneratorService _keysGen;
private IGeocodeService _geocoder;
public ModelFactory(HttpRequestMessage request, IRTWRepository repo,IGeocodeService geocoder)
{
_urlHelper = new UrlHelper(request);
_repo = repo;
_geocoder = geocoder;
}
#region Parses
public MaterialAccepted Parse(MaterialAcceptedModel model)
{
try
{
if (!string.IsNullOrWhiteSpace(model.category))
{
var category = _repo.CategoryRepository.Get(model.category);
if (category == null) return null;
var entry = new MaterialAccepted()
{
business = model.business,
businessService = model.businessService,
residential = model.residential,
residentialService = model.residentialService,
note = model.note,
Category = category
};
return entry;
}
return null;
}
catch
{
return null;
}
}
#endregion
}
我正在使用包含存储库和配置接口的 BaseAPIController
public class BaseApiController : ApiController
{
IRTWRepository _repository;
IModelFactory _modelFactory;
IConfiguration _configuration;
IGeocodeService _geocoder;
public BaseApiController(IRTWRepository repository,IConfiguration configuration)
{
_repository = repository;
_configuration = configuration;
}
protected IRTWRepository TheRepository
{
get
{
return _repository;
}
}
protected IConfiguration TheConfiguration
{
get
{
return _configuration;
}
}
protected IModelFactory TheModelFactory
{
get
{
_geocoder = new GeocodeService(_configuration.GetValue("geocodeGoogleApiKey"));
if (_modelFactory == null)
{
_modelFactory = new ModelFactory(this.Request, _repository,_geocoder);
}
return _modelFactory;
}
}
这是我要测试的控制器中的操作方法
[HttpPost]
[Route("api/recyclecenters/{rcid}/materials/")]
public IHttpActionResult Post(int rcid, [FromBody]MaterialAcceptedModel model)
{
try
{
if (model != null)
{
var recycleCenter = TheRepository.RecycleCenterRepository.Get(rcid);
if (recycleCenter == null)
return NotFound();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var entity = TheModelFactory.Parse(model);
if (entity == null) return BadRequest("Could not read material accepted in body");
if (TheRepository.MaterialAcceptedRepository.Get(recycleCenter.RecycleCenterId, entity.Category.name) != null)
return Conflict();
recycleCenter.Materials.Add(entity);
if (TheRepository.SaveAll())
{
string locationHeader = Url.Link("Materials", new { rcid = rcid, name = model.category.ToLower() });
return Created<MaterialAcceptedModel>(locationHeader, TheModelFactory.Create(entity));
}
return BadRequest("Could not save to the database");
}
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
这是 return 为空的行,即使我在我的测试方法中对其进行了模拟
var entity = TheModelFactory.Parse(model);
这是我的 TestClass
namespace API.Tests.Web
{
[TestClass]
public class MaterialsControllerTest
{
private Mock<IRTWRepository> repository;
private Mock<IModelFactory> factory;
private Mock<IConfiguration> configuration;
private Mock<IRTWAPIIdentityService> identityService;
private MaterialsController controller;
RecycleCenter recycleCenter;
private MaterialAccepted CreateMaterial()
{
return new MaterialAccepted()
{
business = true,
businessService = EnumRecycleCenterService.Dropoff,
residential = false,
residentialService = EnumRecycleCenterService.Pickup,
note = "this a note",
Category = new Category()
{
name = "Books"
}
};
}
[TestInitialize]
public void Initialize()
{
repository = new Mock<IRTWRepository>();
factory = new Mock<IModelFactory>();
configuration = new Mock<IConfiguration>();
identityService = new Mock<IRTWAPIIdentityService>();
controller = new MaterialsController(repository.Object,configuration.Object);
controller.Request = new HttpRequestMessage();
recycleCenter = new RecycleCenter(){RecycleCenterId = 1};
}
[TestMethod]
public void Post_ShouldReturnConflictIfTheRecycleCenterAlreadyTakesMaterial()
{
//arrange
repository.Setup(r => r.RecycleCenterRepository.Get(It.IsAny<int>())).Returns(() => recycleCenter);
factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());
configuration.Setup(c => c.GetValue(It.IsAny<string>())).Returns(() => "APIKEY");
repository.Setup(r => r.MaterialAcceptedRepository.Get(It.IsAny<int>(), It.IsAny<string>())).Returns(() => null);
//act
var actionResult = controller.Post(It.IsAny<int>(),new MaterialAcceptedModel());
//assert
Assert.IsInstanceOfType(actionResult, typeof(ConflictResult));
}
}
}
这是行不通的行,因为它总是 return null 而不是 MaterialAccepted
的新实例
factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());
我试过 f.Parse(It.IsAny()) 但还是不行。
澄清
上面的代码行是 returning null 因为没有模拟 f.Parse() 调用,而是执行它并且 returning null 因为我有 if 条件在那个方法上
谁能解释为什么安装程序不起作用?
Mock 不能直接return null。
诀窍就是创建一个空对象。
假设对象 returned 的类型是 class Material:
Material nullMaterial = null;
...
repository.Setup(r => r.MaterialAcceptedRepository
.Get(It.IsAny<int>(), It.IsAny<string>()))
.Returns(nullMaterial);
这应该可以解决您的问题
您似乎没有将 IModelFactory
注入控制器。您需要确保您的生产代码正在使用您在测试中设置的 Mock。
使用 It.IsAny 设置 Mock 将有效:
factory.Setup(f => f.Parse(It.IsAny<MaterialAcceptedModel>()))
.Returns(() => new MaterialAccepted());
但是,正如@Macilquham 所说,我看不到您在提供的代码中将 Mock 传递给控制器的位置,以便生产代码使用它。
如果您没有在 Mock 对象上调用该方法(实际上您没有),那么您当前是在由您的基 class 创建的真实对象的实例上调用该方法,那么它不管你如何设置你的模拟,它都不会起作用。如果你能够改变你的基础 class,那么做这样的事情可以解决你的问题:
// Add defaulted parameter to base class to allow modelFactory creation
// to be overridden/injected (this will prevent the current default behaviour
// when fetching the factory, if a non-null is passed in)
public BaseApiController(IRTWRepository repository,IConfiguration configuration,
IModelFactory modelFactory = null)
{
_modelFactory = modelFactory;
}
修改您的 sut 构造函数以允许您提供 modelFactory(同样,将其默认为 null),然后适当地修改您的测试:
controller = new MaterialsController(repository.Object,configuration.Object,
factory.Object);
我正在尝试设置对我拥有的 IModelFactory 接口的模拟调用。
这里是 IModelFactory 接口
public interface IModelFactory
{
MaterialAcceptedModel Create(MaterialAccepted model);
MaterialAccepted Parse(MaterialAcceptedModel model);
}
这是实现了 IModelFactor 接口的 ModelFactory class (我在这里只添加了我要测试的方法,不需要添加 Create 方法的实现)
public class ModelFactory : IModelFactory
{
private UrlHelper _urlHelper;
private IRTWRepository _repo;
//private IKeysGeneratorService _keysGen;
private IGeocodeService _geocoder;
public ModelFactory(HttpRequestMessage request, IRTWRepository repo,IGeocodeService geocoder)
{
_urlHelper = new UrlHelper(request);
_repo = repo;
_geocoder = geocoder;
}
#region Parses
public MaterialAccepted Parse(MaterialAcceptedModel model)
{
try
{
if (!string.IsNullOrWhiteSpace(model.category))
{
var category = _repo.CategoryRepository.Get(model.category);
if (category == null) return null;
var entry = new MaterialAccepted()
{
business = model.business,
businessService = model.businessService,
residential = model.residential,
residentialService = model.residentialService,
note = model.note,
Category = category
};
return entry;
}
return null;
}
catch
{
return null;
}
}
#endregion
}
我正在使用包含存储库和配置接口的 BaseAPIController
public class BaseApiController : ApiController
{
IRTWRepository _repository;
IModelFactory _modelFactory;
IConfiguration _configuration;
IGeocodeService _geocoder;
public BaseApiController(IRTWRepository repository,IConfiguration configuration)
{
_repository = repository;
_configuration = configuration;
}
protected IRTWRepository TheRepository
{
get
{
return _repository;
}
}
protected IConfiguration TheConfiguration
{
get
{
return _configuration;
}
}
protected IModelFactory TheModelFactory
{
get
{
_geocoder = new GeocodeService(_configuration.GetValue("geocodeGoogleApiKey"));
if (_modelFactory == null)
{
_modelFactory = new ModelFactory(this.Request, _repository,_geocoder);
}
return _modelFactory;
}
}
这是我要测试的控制器中的操作方法
[HttpPost]
[Route("api/recyclecenters/{rcid}/materials/")]
public IHttpActionResult Post(int rcid, [FromBody]MaterialAcceptedModel model)
{
try
{
if (model != null)
{
var recycleCenter = TheRepository.RecycleCenterRepository.Get(rcid);
if (recycleCenter == null)
return NotFound();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var entity = TheModelFactory.Parse(model);
if (entity == null) return BadRequest("Could not read material accepted in body");
if (TheRepository.MaterialAcceptedRepository.Get(recycleCenter.RecycleCenterId, entity.Category.name) != null)
return Conflict();
recycleCenter.Materials.Add(entity);
if (TheRepository.SaveAll())
{
string locationHeader = Url.Link("Materials", new { rcid = rcid, name = model.category.ToLower() });
return Created<MaterialAcceptedModel>(locationHeader, TheModelFactory.Create(entity));
}
return BadRequest("Could not save to the database");
}
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
这是 return 为空的行,即使我在我的测试方法中对其进行了模拟
var entity = TheModelFactory.Parse(model);
这是我的 TestClass
namespace API.Tests.Web
{
[TestClass]
public class MaterialsControllerTest
{
private Mock<IRTWRepository> repository;
private Mock<IModelFactory> factory;
private Mock<IConfiguration> configuration;
private Mock<IRTWAPIIdentityService> identityService;
private MaterialsController controller;
RecycleCenter recycleCenter;
private MaterialAccepted CreateMaterial()
{
return new MaterialAccepted()
{
business = true,
businessService = EnumRecycleCenterService.Dropoff,
residential = false,
residentialService = EnumRecycleCenterService.Pickup,
note = "this a note",
Category = new Category()
{
name = "Books"
}
};
}
[TestInitialize]
public void Initialize()
{
repository = new Mock<IRTWRepository>();
factory = new Mock<IModelFactory>();
configuration = new Mock<IConfiguration>();
identityService = new Mock<IRTWAPIIdentityService>();
controller = new MaterialsController(repository.Object,configuration.Object);
controller.Request = new HttpRequestMessage();
recycleCenter = new RecycleCenter(){RecycleCenterId = 1};
}
[TestMethod]
public void Post_ShouldReturnConflictIfTheRecycleCenterAlreadyTakesMaterial()
{
//arrange
repository.Setup(r => r.RecycleCenterRepository.Get(It.IsAny<int>())).Returns(() => recycleCenter);
factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());
configuration.Setup(c => c.GetValue(It.IsAny<string>())).Returns(() => "APIKEY");
repository.Setup(r => r.MaterialAcceptedRepository.Get(It.IsAny<int>(), It.IsAny<string>())).Returns(() => null);
//act
var actionResult = controller.Post(It.IsAny<int>(),new MaterialAcceptedModel());
//assert
Assert.IsInstanceOfType(actionResult, typeof(ConflictResult));
}
}
}
这是行不通的行,因为它总是 return null 而不是 MaterialAccepted
的新实例 factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());
我试过 f.Parse(It.IsAny()) 但还是不行。
澄清
上面的代码行是 returning null 因为没有模拟 f.Parse() 调用,而是执行它并且 returning null 因为我有 if 条件在那个方法上
谁能解释为什么安装程序不起作用?
Mock 不能直接return null。 诀窍就是创建一个空对象。 假设对象 returned 的类型是 class Material:
Material nullMaterial = null;
...
repository.Setup(r => r.MaterialAcceptedRepository
.Get(It.IsAny<int>(), It.IsAny<string>()))
.Returns(nullMaterial);
这应该可以解决您的问题
您似乎没有将 IModelFactory
注入控制器。您需要确保您的生产代码正在使用您在测试中设置的 Mock。
使用 It.IsAny 设置 Mock 将有效:
factory.Setup(f => f.Parse(It.IsAny<MaterialAcceptedModel>()))
.Returns(() => new MaterialAccepted());
但是,正如@Macilquham 所说,我看不到您在提供的代码中将 Mock 传递给控制器的位置,以便生产代码使用它。
如果您没有在 Mock 对象上调用该方法(实际上您没有),那么您当前是在由您的基 class 创建的真实对象的实例上调用该方法,那么它不管你如何设置你的模拟,它都不会起作用。如果你能够改变你的基础 class,那么做这样的事情可以解决你的问题:
// Add defaulted parameter to base class to allow modelFactory creation
// to be overridden/injected (this will prevent the current default behaviour
// when fetching the factory, if a non-null is passed in)
public BaseApiController(IRTWRepository repository,IConfiguration configuration,
IModelFactory modelFactory = null)
{
_modelFactory = modelFactory;
}
修改您的 sut 构造函数以允许您提供 modelFactory(同样,将其默认为 null),然后适当地修改您的测试:
controller = new MaterialsController(repository.Object,configuration.Object,
factory.Object);