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 :)
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;
}
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
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);
}
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));
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):
- 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 :-)
- 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.
精彩评论