GregH
GregH

Reputation: 5459

Mocked object returning null despite specifying Returns()

I am currently working with Moq to do some unit testing. I am running into an issue where I am specifying what my mocked object returns, but the actual call is returning null instead of what I am specifying in .Returns(...). I've reviewed other posts and one of the suggestions was to create the mock with MockBehavior.Strict- after doing this, I get a fairly verbose error as follows:

IMyRepository.Save(MvcIndividualAuth.Data.Models.DTO.MyTableDTO) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.`

However, I am calling setup on the only method that my mocked object calls already. Please see code below:

My test:

MyService _myService;
Mock<IMyRepository> _myRepoMock;

[TestInitialize]
public void Setup()
{
  _myRepoMock = new Mock<IMyRepository>();
  _myService = new MyService(_myRepoMock.Object);
}

[TestMethod]
public void MyServiceSave()
{
  //Arrange
  var myDto = new MyTableDTO { Id = 1, Bar = 5, Foo = "Test" };
  _myRepoMock.Setup(x => x.Save(myDto)).Returns(myDto);

  //Act
  var vm = _myService.Save(new MyTableViewModel(myDto));

  //Assert
  Assert.AreEqual(vm.Id, 1);
  Assert.AreEqual(vm.Foo, "Test");
  Assert.AreEqual(vm.Bar, 5);
  Assert.AreEqual(vm.BarPlusFoo, "5 Test");
}

MyService.Save method:

public MyTableViewModel Save(MyTableViewModel viewModel)
{
  var dto = MyTableViewModel.GetDto(viewModel);
  var dbDto = _myRepo.Save(dto);    //_myRepo is of type IMyRepository, 
                                    // this _myRepo.Save call is returning null

  var vm = new MyTableViewModel(dbDto);
  
  return vm;
}

Why is the mocked repo in my test not returning the value I specify in my Returns(..) call? All help is appreciated.

Update: as requested, here is MyRepository.Save method and MyTableViewModel.GetDto():

MyRepository.Save:

public MyTableDTO Save(MyTableDTO dto)
{
  try
  {
    var entity = new MyTable();

    if (String.IsNullOrEmpty(dto.Foo))
    {
      throw new ArgumentException("MyTable requires Foo");
    }

    if (dto.Id == 0)
    {
      //added
      entity.Update(dto);
      _db.MyTables.Add(entity);
    }
    else
    {
      //modified
      entity = _db.MyTables.Single(x => x.Id == dto.Id);

      entity.Update(dto);
    }

    _db.SaveChanges();

    return new MyTableDTO(entity);
  }
  catch (Exception)
  {
    throw;
  }
}

MyTableViewModel.GetDto(..);

public static MyTableDTO GetDto(MyTableViewModel vm)
{
  var dto =  new MyTableDTO
  {
    Bar = vm.Bar,
    Foo = vm.Foo,
    Id = vm.Id
  };

  return dto;
}

Upvotes: 3

Views: 3301

Answers (2)

Roman
Roman

Reputation: 12171

You get null because GetDto() returns object different from myDto - references are different.

You can change your Setup() to return myDto:

_myRepoMock.Setup(x => x.Save(It.IsAny<MyTableDTO>())).Returns(myDto);

Or if you want to return object which was passed as a parameter:

_myRepoMock.Setup(x => x.Save(It.IsAny<MyTableDTO>())).Returns((MyTableDTO dto) => dto);

Or if you want to mock based on some properties:

_myRepoMock.Setup(x => x.Save(It.Is<MyTableDTO>(dto => dto.Id == 1))).Returns(myDto);

Or if you want to modify return result:

_myRepoMock.Setup(x => x.Save(It.IsAny<MyTableDTO>())).Returns((MyTableDTO dto) => { dto.Id = 2; return dto;});

You can also combine all approaches.

Upvotes: 5

Owen Pauling
Owen Pauling

Reputation: 11841

The MyTableDTO returned by GetDTO is a new MyTableDTO which is not the same as the rule in your Setup, because it has a different reference, hence there is no matching setup for Save.

Instead you can try something like:

_myRepo.Setup(s => s.Save(It.Is<MyTableDTO>(d => d.Equals(myDto))).Returns(myDto); 

Or, if you are not concerned about the exact values passed to Save:

_myRepo.Setup(s => s.Save(It.IsAny<MyTableDTO>()).Returns(myDto); 

Upvotes: 3

Related Questions