46

I have had a few occasions where something like this would be helpful. I have, for instance, an AccountCreator with a Create method that takes a NewAccount. My AccountCreator has an IRepository that will eventually be used to create the account. My AccountCreator will first map the properties from NewAccount to Account, second pass the Account to the repo to finally create it. My tests look something like this:

public class when_creating_an_account
{
    static Mock<IRepository> _mockedRepository;
    static AccountCreator _accountCreator;
    static NewAccount _newAccount;
    static Account _result;
    static Account _account;

    Establish context = () =>
        {
            _mockedRepository = new Mock<IRepository>();
            _accountCreator = new AccountCreator(_mockedRepository.Object);

            _newAccount = new NewAccount();
            _account = new Account();

            _mockedRepository
                .Setup(x => x.Create(Moq.It.IsAny<Account>()))
                .Returns(_account);
        };

    Because of = () => _result = _accountCreator.Create(_newAccount);

    It should_create_the_account_in_the_repository = () => _result.ShouldEqual(_account);
}

So, what I need is something to replace It.IsAny<Account>, because that doesn't help me verify that the correct Account was created. What would be amazing is something like...

public class when_creating_an_account
{
    static Mock<IRepository> _mockedRepository;
    static AccountCreator _accountCreator;
    static NewAccount _newAccount;
    static Account _result;
    static Account _account;

    Establish context = () =>
        {
            _mockedRepository = new Mock<IRepository>();
            _accountCreator = new AccountCreator(_mockedRepository.Object);

            _newAccount = new NewAccount
                {
                    //full of populated properties
                };
            _account = new Account
                {
                    //matching properties to verify correct mapping
                };

            _mockedRepository
                .Setup(x => x.Create(Moq.It.IsLike<Account>(_account)))
                .Returns(_account);
        };

    Because of = () => _result = _accountCreator.Create(_newAccount);

    It should_create_the_account_in_the_repository = () => _result.ShouldEqual(_account);
}

Notice I changed It.IsAny<> to It.IsLike<> and passed in a populated Account object. Ideally, in the background, something would compare the property values and let it pass if they all match.

So, does it exist already? Or might this be something you have done before and wouldn't mind sharing the code?

Byron Sommardahl
  • 12,743
  • 15
  • 74
  • 131
  • 1
    Moq supports custom matchers - as in, you can have custom comparers used when matching arguments for a call, but you have to implement that yourself. See example [here](http://stackoverflow.com/a/10300051/343266). – k.m Jul 02 '12 at 10:10

5 Answers5

51

To stub out a repository to return a particular value based on like criteria, the following should work:

_repositoryStub
    .Setup(x => x.Create(
        Moq.It.Is<Account>(a => _maskAccount.ToExpectedObject().Equals(a))))
    .Returns(_account);
Dan Esparza
  • 28,047
  • 29
  • 99
  • 127
Derek Greer
  • 15,454
  • 5
  • 45
  • 52
  • 1
    Now, THAT'S what I was looking for! Thanks Derek! – Byron Sommardahl Jul 05 '12 at 21:32
  • 8
    In case anyone stumbled across this answer and wants to try the same thing, you've got to include the ExpectedObjects library. https://github.com/derekgreer/expectedObjects or `install-package ExpectedObjects` – Byron Sommardahl Jul 09 '12 at 17:12
  • 1
    You can wrap this as a method for reuse: `T IsEquivalent(object expected) => It.Is(x => expected.ToExpectedObject().Equals(x))` – Arithmomaniac Dec 07 '18 at 01:31
  • You can also do ```public static T Equivalent(T expected) => Match.Create (s => expected.ToExpectedObject().Equals(s));``` – Ian1971 Jul 30 '20 at 09:59
  • Great! Any clever way to check what property is mis-matching when the test fails? For example the invocation on the method is called but apparently a property is mismatching. – CularBytes Sep 18 '20 at 09:05
  • 1
    The intended use of ExpectedObjects is to verify indirect inputs and outputs, so this is a rather novel application of its use. If you want to assert that an indirect input was a specific value, you'd need to capture the value and use the ShouldEquals() method. Of course, if you knew you were only getting one call and you just wanted to temporarily see why the stub wasn't triggering you could use ShouldEqual() in the setup predicate and just return a boolean to satisfy the predicate. It would seem easier to just step through the code though. – Derek Greer Sep 18 '20 at 13:44
  • The previous comment should have read "The intended use of ExpectedObjects is to verify the _direct or indirect_ inputs and outputs ...". The point, however, wasn't about what role the object being compared played in the overall test collaboration, but rather that the library was originally envisioned as an assertion library, not as a general-purpose value-based equality library. It just so happens it can also function as such. – Derek Greer Feb 27 '21 at 14:27
20

The following should work for you:

Moq.It.Is<Account>(a=>a.Property1 == _account.Property1)

However as it was mentioned you have to implement matching criteria.

k0stya
  • 4,267
  • 32
  • 41
  • 5
    I use that every day. But imagine "a" is a class with 50 properties. I would rather be able to use the expected object pattern to compare the actual object (with all it's properties) to an expected object (with all it's expected properties). That's what I'm holding out for. – Byron Sommardahl Jul 05 '12 at 17:32
  • Doing this will also pass if more properties are added to the model but not to the test comparison, which is probably undesirable. – Town Nov 27 '18 at 16:41
6

I have done that using the FluentAssertians library (which is much more flexible and has lot's of goodies), as in the following example:

_mockedRepository
        .Setup(x => x.Create(Moq.It.Is<Account>(actual =>
                   actual.Should().BeEquivalentTo(_account, 
                       "") != null)))
        .Returns(_account);

Note the empty argument, which is needed since this is a lambda expression which can't use default parameters.

Also note the != null expression which is just to convert it to bool so to be able to compile, and to be able to pass when it is equal, since when it is not equal then FluentAssertians will throw.


Note that this only works in newer versions of FluentAssertians, for older versions you can do a similar method described in http://www.craigwardman.com/Blogging/BlogEntry/using-fluent-assertions-inside-of-a-moq-verify

It involves using an AssertionScope as in the following code

public static class FluentVerifier
{
    public static bool VerifyFluentAssertion(Action assertion)
    {
        using (var assertionScope = new AssertionScope())
        {
             assertion();

             return !assertionScope.Discard().Any();
        }
    }
 }

And then you can do:

_mockedRepository
            .Setup(x => x.Create(Moq.It.Is<Account>(actual => 
                   FluentVerifier.VerifyFluentAssertion(() =>
                       actual.Should().BeEquivalentTo(_account, "")))))))
            .Returns(_account);
yoel halb
  • 12,188
  • 3
  • 57
  • 52
0

I was not able to find anything that does exactly what is described in the question. In the mean time, the best way I can find to handle verification of objects passed in as arguments to mocked methods (without the luxury of referencial equality) is a combination of Callback and the Expected Object pattern to compare the actual with the expected object:

public class when_creating_an_account
{
    static Mock<IRepository> _mockedRepository;
    static AccountCreator _accountCreator;
    static NewAccount _newAccount;
    static Account _result;
    static Account _expectedAccount;
    static Account _actualAccount;

    Establish context = () =>
        {
            _mockedRepository = new Mock<IRepository>();
            _accountCreator = new AccountCreator(_mockedRepository.Object);

            _newAccount = new NewAccount
                {
                    //full of populated properties
                };
            _expectedAccount = new Account
                {
                    //matching properties to verify correct mapping
                };

            _mockedRepository
                .Setup(x => x.Create(Moq.It.IsAny<Account>(_account)))
                //here, we capture the actual account passed in.
                .Callback<Account>(x=> _actualAccount = x) 
                .Returns(_account);
        };

    Because of = () => _result = _accountCreator.Create(_newAccount);

    It should_create_the_account_in_the_repository = 
        () => _result.ShouldEqual(_account);

    It should_create_the_expected_account = 
        () => _expectedAccount.ToExpectedObject().ShouldEqual(_actualAccount);
}

The expected object pattern is great, but it's complicated to implement in C#, so I use a library that handles all that for me. https://github.com/derekgreer/expectedObjects

My last observation looks at the properties in the actual account passed in and compares each to the same property on my "expected object". This way I don't have a huge list of mock property checks, nor do I have a ton of test observations.

Byron Sommardahl
  • 12,743
  • 15
  • 74
  • 131
0

If I use JSONConvert.SerializeObject to covert the expected and actual objects to JSON strings, and then do an equals between them, it does seem to produce an acceptable result. My thinking is that if a string representation of the objects match, then their public properties are most likely the same too.

Shafiq Jetha
  • 1,337
  • 16
  • 30