无法让Moq安装程序返回null

时间:2016-03-11 07:04:47

标签: c# entity-framework unit-testing moq

我正在尝试将Mock调用设置为我拥有的IModelFactory接口。

这里是IModelFactory接口

public interface IModelFactory
    {

       MaterialAcceptedModel Create(MaterialAccepted model);                                           
       MaterialAccepted Parse(MaterialAcceptedModel model);        
  }

这是实现IModelFactor接口的ModelFactory类(我只在这里添加我要测试的方法,不需要添加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
    }

我正在使用包含repo和配置接口的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);
            }

        }

这是返回null的行,即使我在我的测试方法

上嘲笑它
  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));
        }

    }
}

这行不起作用,因为它总是返回null而不是MaterialAccepted的新实例

 factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());

我尝试了f.Parse(It.IsAny()),但仍然无效。

澄清

上面的代码行返回null,因为它没有模拟f.Parse()调用,而是执行它并返回null,因为我对该方法的if条件

任何人都可以解释为什么安装程序无法正常工作?

3 个答案:

答案 0 :(得分:3)

使用It.IsAny设置你的模拟将起作用:

factory.Setup(f => f.Parse(It.IsAny<MaterialAcceptedModel>()))
       .Returns(() => new MaterialAccepted());

然而,正如@Macilquham所说,我无法看到你在提供的代码中将Mock传递给控制器​​的位置,以便生产代码使用它。

如果你没有在你的Mock对象上调用你没有的方法,你当前正在基类创建的真实对象实例上调用该方法,那么你怎么没关系设置你的模拟它不会起作用。如果您能够更改基类,那么执行此类操作将允许您解决问题:

// 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);

答案 1 :(得分:2)

您似乎没有在IModelFactory中注入控制器。您需要确保您的生产代码使用您在测试中设置的模拟。

答案 2 :(得分:-2)

模拟不能直接返回null。 诀窍就是创建一个null对象。 假设返回的对象是 class Material

类型
    Material nullMaterial = null;
    ...
    repository.Setup(r => r.MaterialAcceptedRepository
      .Get(It.IsAny<int>(), It.IsAny<string>()))
      .Returns(nullMaterial);  

这应该可以解决您的问题