Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage of Nunit SameAsConstraint (Is.SameAs(...)) in mock verification leads to incorrect result #853

Closed
AndiKleini opened this issue Jun 30, 2019 · 3 comments

Comments

@AndiKleini
Copy link

By checking equality of references, I faced a problem when I used NUnit SameAsConstraint (Is.SameAs(...)) in mocks verification method. The assertion evaluates to false, although both references are equal. By switching from NUnit SameAsConstraint to a direct comparison (e.g.: It.Is(input => input.Equals(sameReferenceAsInput)) performs flawless.
I created a small example to reproduce the issue:
Lets look at a simple class that adds two numbers an emits a proper event when calculation is performed. The sender argument of the event references the event sending instance following .Net framework design guidelines:


   public class SomeService    
   {
        public event EventHandler<EventArgs> OnAdd;
        public int Add(int left, int right)
        {
            if (this.OnAdd != null)
            {
                this.OnAdd(this, new EventArgs());
            } 
            return left + right;
        }
   }

Then look at both tests doing exactly the same. The only difference is implementation of assertions:

/// <summary>
/// Contains two tests exploring the behaviour od Nuint SameAsConstrain
/// in comparison to dirtly checking for references equality.
/// </summary>
[TestFixture]
public class SomeServiceTest
{

    /// <summary>
    /// This tests verifies whether the Event OnAdd was triggered
    /// when the Add operation of the service is invoked.
    /// The failing of the test is incorrect and Is.SameAs 
    /// (Nunit SameAsConstraint) reports and error, although 
    /// compared references are equal.
    /// </summary>
    [Test]
    public void FailingTest_DueToWrongEvalutionOfSameAsConstraint()
    {
        var instanceUnderTest = new SomeService();

        var mockHandler = new Mock<IOnAddEventHanlder>();

        instanceUnderTest.OnAdd += mockHandler.Object.HandleOnAdd;

        instanceUnderTest.Add(1, 2);

        mockHandler.Verify(m => m.HandleOnAdd(
            Is.SameAs(instanceUnderTest),
            It.IsAny<EventArgs>()));
    }

    /// <summary>
    /// This tests verifies whether the Evnet OnAdd was triggered
    /// when the Add operation of the service is invoked.
    /// This test executes as expected. The compared references are
    /// equal and consequently our assertion does not fail.
    /// </summary>
    [Test]
    public void SucceedingTest_DueToExecutingReferenceEqualsDirectly()
    {
        var instanceUnderTest = new SomeService();
        var mockHandler = new Mock<IOnAddEventHanlder>();
        instanceUnderTest.OnAdd += mockHandler.Object.HandleOnAdd;

        instanceUnderTest.Add(1, 2);

        mockHandler.Verify(m => m.HandleOnAdd(
            It.Is<object>(o => o.Equals(instanceUnderTest)),
            It.IsAny<EventArgs>()));
    }
}

In order to mock up on the event i created a helper interface (available only in test solution):

public interface IOnAddEventHanlder
 {
     void HandleOnAdd(object sender, EventArgs args);
 }

As mentioned above:
SucceedingTest_DueToExecutingReferenceEqualsDirectly -> succeeds which is OK
FailingTest_DueToWrongEvalutionOfSameAsConstraint -> fails which is not OK

I'm using:

  • VS 2017
  • Nuint 3.12.0
  • Moq 4.12.0
  • NUnit3TestAdapter 3.13.0
  • Microsoft.NET.Test.SDK 16.2.0
  • .NET Core 2.2.0

@stakx
Copy link
Contributor

stakx commented Jun 30, 2019

I haven't tested your code yet, but I assume that the problem is that you are attempting to use NUnit's Is.SameAs like a Moq argument matcher when it isn't one. So what's likely happening is that Moq will match the actual recorded argument value (an instance of SomeService) against the value produced by invoking Is.SameAs(instanceUnderTest) (an instance of SameAsConstraint).

You could wrap NUnit constraints as custom Moq argument matchers (which are basically functions calling Moq.Match.Create<TArg>), but is that really necessary? Since you're comparing raw values you don't really need any matcher at all for that first argument:

mockHandler.Verify(m => m.HandleOnAdd(
    instanceUnderTest,
    It.IsAny<EventArgs>()));

@AndiKleini
Copy link
Author

Thanks a lot for your quick reply. This was a misunderstanding on my side. I will close the issue.

@stakx
Copy link
Contributor

stakx commented Jun 30, 2019

No worries, glad I could help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants