0

I'm using Moq in my unit tests, and I can't figure out why my test is failing.

Here's the error I'm getting

Moq.MockVerificationException : The following setups were not matched:
IGroupRepository r => r.Delete(It.Is(um => um.Equals()))

Here's the service I'm trying to test.

Service

public GroupService ( IGroupRepository groupRepository )
{
    _groupModelToViewModelMapper = new AutoMapper<GroupModel, GroupViewModel>();
    _groupViewModelToModelMapper = new AutoMapper<GroupViewModel, GroupModel>();
    _groupRepository = groupRepository;
}

public void Delete( GroupViewModel viewModel )
{
    _groupRepository.Delete( _groupViewModelToModelMapper.BuildFrom( viewModel ) );
}

Test

[Test]
public void FAILING_TEST ()
{
    // Setup the Mock GroupRepository
    _groupRepository.Setup( r => r.Delete( It.Is<GroupModel>( um => um.Equals( _groupModel ) ) ) );

    var groupService = new GroupService( _groupRepository.Object);

    // execute
    groupService.Delete( _groupViewModel );

    // assert
    _groupRepository.VerifyAll();
}

The thing I can't figure out is, if I Inject the AutoMapper, the tests pass. I don't see the point in injecting the automapper since it's basically a one-to-one with the service models it's mapping.

Service

public GroupService( IGroupRepository groupRepository,
                     IAutoMapper<GroupViewModel, GroupModel> groupViewModelToModelMapper,
                     IAutoMapper<GroupModel, GroupViewModel> groupModelToViewModelMapper )
{
    _groupModelToViewModelMapper = groupModelToViewModelMapper;
    _groupRepository = groupRepository;
    _groupViewModelToModelMapper = groupViewModelToModelMapper;
}

public void Delete( GroupViewModel viewModel )
    {
        _groupRepository.Delete( _groupViewModelToModelMapper.BuildFrom( viewModel ) );
    }

Test

[Test]
public void PASSING_TEST()
{
    // Setup the Mock GroupRepository
    _groupRepository.Setup(r => r.Delete(It.Is<GroupModel>(um => um.Equals(_groupModel))));

    // Setup the Mock ModelMapper
    _viewModelToModelMapper.Setup(vmm => vmm.BuildFrom(It.Is<GroupViewModel>(u => u.Equals(_groupViewModel))))
                          .Returns(_groupModel);

    var groupService = new GroupService(_groupRepository.Object, _viewModelToModelMapper.Object, _modelToViewModelMapper.Object);

    // execute
    groupService.Delete(_groupViewModel);

    // assert
    _groupRepository.VerifyAll();
    _viewModelToModelMapper.VerifyAll();
}

Also, if it's valid, here's the Test SetUp.

private GroupModel _groupModel;
private List<GroupModel> _groupModels;
private GroupViewModel _groupViewModel;
private List<GroupViewModel> _groupViewModels;
private Mock<IGroupRepository> _groupRepository;
private Mock<IAutoMapper<GroupViewModel, GroupModel>> _viewModelToModelMapper;
private Mock<IAutoMapper<GroupModel, GroupViewModel>> _modelToViewModelMapper;


[SetUp]
public void SetUp()
{
    _groupModel = new GroupModel();
    _groupModels = new List<GroupModel>();
    _groupViewModel = new GroupViewModel();
    _groupViewModels = new List<GroupViewModel>();
    _groupRepository = new Mock<IGroupRepository>();
    _viewModelToModelMapper = new Mock<IAutoMapper<GroupViewModel, GroupModel>>();
    _modelToViewModelMapper = new Mock<IAutoMapper<GroupModel, GroupViewModel>>();

}

Can somebody help me get the hang of this mocking thing?

Chase Florell
  • 46,378
  • 57
  • 186
  • 376
  • Random guess - your `Equals` is not comparing objects by value and `um => um.Equals(_groupModel)` fails when model is generated instead of explicitly returned as in passing test. – Alexei Levenkov Oct 02 '13 at 21:48
  • SO why would it pass if I inject the modelMappers but not pass if I instantiate them in the service constructor? – Chase Florell Oct 02 '13 at 21:49
  • `_groupModel.Equals(_groupModel)` is true fro default pointer comparison (that is what happen when you inject mapper that simply returns _groupModel), but `_groupModel.Equals(new Model(fromSomeData))` will always fail default pointer comparison... – Alexei Levenkov Oct 02 '13 at 22:24
  • If you would, could you throw the answer below? I"m not sure I totally follow just yet. – Chase Florell Oct 02 '13 at 22:26

2 Answers2

2

Just override Equals (and GetHashCode) of your GroupModel class and your test will pass. By default Equals uses comparison based on object's references. Thus you have two different instances (_groupModel and one built by mapper) Equals returns false.

In your passing example you are comparing exactly same instance _groupModel, so Equals returns true.

Chase Florell
  • 46,378
  • 57
  • 186
  • 376
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • What "might" the overridden `Equals` and `GetHashCode` look like? – Chase Florell Oct 03 '13 at 00:12
  • 1
    @ChaseFlorell Equals should implement logic of treating instances as equal - it could be simple id comparison, or comparison of all fields. [Hash code generation is a little bit more tricky](http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode) when you use several values for comparison in Equals (if you use only id for equals, then you can use id.GetHashCode for object hash code). – Sergey Berezovskiy Oct 03 '13 at 06:51
1

When I was mocking everything including the AutoMapper, it was good enough to use a Reference comparison.

_groupRepository.Setup( r => r.Delete( It.Is<GroupModel>( um => um.Equals( _groupModel ) ) ) );

Now that We're actually using the the AutoMapper not mocking it and injecting it, I had to do a value comparison.

_groupRepository.Setup( r => r.Delete( It.Is<GroupModel>( um => um.Id == groupId ) ));

This solves the problem.

Chase Florell
  • 46,378
  • 57
  • 186
  • 376
  • That is exactly what I meant... Note that lazyberezovsky have good suggestion which may be what you need in the rest of the project too (if you want to treat `GroupModel` more like value type). If it is just for mocks - comparing one interesting property is perfectly valid approach. – Alexei Levenkov Oct 03 '13 at 00:03
  • Thanks @AlexeiLevenkov... it just took a little thinking. I've been staring at the screen too long today – Chase Florell Oct 03 '13 at 00:10