Moq - How to verify that a property value is set via the setter

C#.NetMockingTddMoq

C# Problem Overview


Consider this class:

public class Content
{      
   public virtual bool IsCheckedOut {get; private set;}
   public virtual void CheckOut()
   {
      IsCheckedOut = true;
   }

   public virtual void CheckIn()
   {
      //Do Nothing for now as demonstrating false positive test.
   }
}

The Checkin method is intentionally empty. Now i have a few test methods to verify the status of calling each method.

[TestMethod]
public void CheckOutSetsCheckedOutStatusToTrue()
{
    Content c = new Content();    
    c.CheckOut();
    Assert.AreEqual(true, c.IsCheckedOut); //Test works as expected
}

[TestMethod]
public void CheckInSetsCheckedOutStatusToFalse()
{
    Content c = new Content();
    c.CheckIn();
    Assert.AreEqual(false, c.IsCheckedOut); //Test does not work as expected
}

The 2nd test passes for the wrong reasons. So how can i use mocking (moq) to verify that CheckIn is setting the IsCheckedOut property?

Thanks.

EDIT

To clarify: I have a method called CheckIn() whose job it is to set the IsCheckedOut status to false.

You will see in my test code above that the Test will return false even if i do not set the property value to false; This is expected, nothing wrong here.

I think my question specifically is How can i verify that the CheckIn() method has set the IsCheckedOut property to false? This is what I would call behavioral verification.

I believe some of the comments suggested doing something which amounts to state verification? If so I don't believe there is any value in mocking this part at all when we can simply use:

Content c = new Content();    
c.CheckIn();    
Assert.AreEqual(false, c.IsCheckedOut); //State verification

Of course I may be wrong, so please help me clarify these concepts :)

C# Solutions


Solution 1 - C#

The following should work. Configure your mock object as:

var mock=new Mock<IContent>();
mock.SetupSet(content => content.IsCheckedOut=It.IsAny<bool>()).Verifiable();

And after the test code:

mock.VerifySet(content => content.IsCheckedOut=It.IsAny<bool>());

I haven't tested it anyway, so please tell me if it works for you.

EDIT. Indeed, this will not work since the setter for IsCheckedOut is false.

Anyway, now I see that you never set the value of IsCheckedOut at class construction time. It would be a good idea to add the following to the Content class:

public Content()
{
    IsCheckedOut=false;
}

Solution 2 - C#

Mock mockContect = new Mock<Cotent>(); 
mockContent.VerifySet(x => x.IsCheckedOut, Times.Once());

Will that do the trick? Not sure how the private setter comes in to play as havent tested that. but works for my public setter.

Got this from: http://www.codethinked.com/post/2009/03/10/Beginning-Mocking-With-Moq-3-Part-2.aspx

Solution 3 - C#

why don't you simply set up the content to be checked out to start with? Remember, you are only testing the behaviour of the CheckIn function.

[TestMethod]
public void CheckInSetsCheckedOutStatusToFalse()
{
    // arrange - create a checked out item
    Content c = new Content();
    c.CheckOut();

    // act - check it in
    c.CheckIn();

    // assert - IsCheckedOut should be set back to false
    Assert.AreEqual(false, c.IsCheckedOut);
}

Solution 4 - C#

Can I suggest that you might be thinking about this in the wrong way - generally you should be setting something up, performing an action and then checking the behaviour (result). In this case does it really matter that it wasn't set to false by the setter - what should matter is that it is false in after a given scenario has been exercised. If you take tests in isolation this might seem a bit odd, but for anything your tests will exist in sets.

The situation would be different if you were testing the interaction between two classes - then it would be fine to set up an expectation on the property setter - as the setting action is the interaction you're testing.

I'm not familiar with Moq as I use Rhino.Mocks - but I'm guessing there'll be something along the lines of mock.VerifySet(content => content.IsCheckedOut=It.IsEqual(true));

Solution 5 - C#

I agree with you: mocking has no value in this scenario because it is intended to test the interactions between your class (under test) and the rest of the world, not to test the inner mechanism of your class.

I think that this test

Content c = new Content();    
c.CheckIn();    
Assert.AreEqual(false, c.IsCheckedOut); //State verification

that you write has sense and it is not a false positive! You must ensure that the state is in that way after the CheckIn regardless on why it is so; if in the future you will set the state in the constructor (or in other methods) this test will save you and you will be forced to implement the CheckIn method!

In some cases similar to your I want to set the initial state to be sure that I does not forget to implement the CheckIn method; in this case I use 2 methods (the first is very ugly):

  1. I call c.CheckOut() before c.CheckIn(); this is very ugly, because you test 2 methods instead of one ... but I admit that I wrote something similar few times :-)
  2. I make the private setter protected, and I write a test class that inherits from the class under test; in this manner I can set the property to true before to call c.CheckIn() to ensure that the method is doing his job.

Here it is the code:

    public class Content2
{
    public virtual bool IsCheckedOut { get; protected set; }
    public virtual void CheckOut()
    {
        IsCheckedOut = true;
    }

    public virtual void CheckIn()
    {
        //Do Nothing for now as demonstrating false positive test.
    } 
}

    [TestClass]
public class Content2Test : Content2
{
    [TestMethod]
    public void CheckOutSetsCheckedOutStatusToTrue()
    {
        this.CheckOut();
        Assert.AreEqual(true, this.IsCheckedOut); //Test works as expected
    }

    [TestMethod]
    public void CheckInSetsCheckedOutStatusToFalse()
    {
        this.IsCheckedOut = true;
        this.CheckIn();
        Assert.AreEqual(false, this.IsCheckedOut); //Test does not work as expected
    }
}

Hope to help.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAnton PView Question on Stackoverflow
Solution 1 - C#KonamimanView Answer on Stackoverflow
Solution 2 - C#DominicView Answer on Stackoverflow
Solution 3 - C#Mark HeathView Answer on Stackoverflow
Solution 4 - C#FinnNkView Answer on Stackoverflow
Solution 5 - C#Daniele ArmanascoView Answer on Stackoverflow