views:

843

answers:

3

Hi

I been looking at the Moq documentation and to me the comments are too short for me to understand each of things it can do.

First thing I don't get is It.IsAny<string>(). //example using string

Is there an advantage of using this over just putting some value in? Like I know people say use this if you don't care about the value but if you don't care about the value can't you just do "a" or something? Like this just seems like more typing.

Second when would be an example of when you would not care about the value? I thought Moq needs the value to match up stuff.

I don't get what It.Is<> is for at all or how to use it. I don't get the example and what it is trying to show.

Next I don't get when to use the Timer stuff like I see it has like AtMost and stuff like that. Why would you limit the number of times something is setup? Like for me I have some AppConfig value that I need to use. I think I use it like 2 times. Why would I want to limit to say 1? This would just make the test fail. Is this to stop other people from adding another one to your code or something?

I don't get how to use mock.SetupAllProperties();

What does it setup the properties with?

I don't also get why they got so many different ways to setup a property and what the difference is.

Like in the documentation it has:

SetupGet(of property)
SetupGet<TProperty>

I noticed that alot of the stuff in Moq has always these ways () and <> whats the difference between the and what would they look like?

I also don't get why they have setupGet. Would you not use SetupSet to set a property?

SetupSet has like 5 ways in the documentation. Plus another one called SetupProperty.

So I don't understand why there are so many.

On 2 sides notes I am wondering if lambdas as the variables in them, are they independent of other lambdas.

Like if U have this

mock.setup(m => m.Test);

stop.setup(m => m.Test);

Would this be ok or would there be some conflict?

I was watching this video and I am wondering is he using Visual Studio? Like his intellisense looks different. A lightblub pops up for him (I am happy mine does not it brings back painful memories of netbeans), there are lines going from one opening brace to the closing brace and etc.

Thanks :)

A: 

If you don't care about the exact value of a property, it's far better to use .IsAny because you are being explicit about the fact that the exact value is not important. If you hardcode it as "abc", then it is not clear if your code you are testing depends on starting with "a" or ending with "c" or being 3 chars long, etc. etc.

Andy_Vulhop
+23  A: 

It.IsAny / It.Is

These can be useful when you're passing a new reference type within the code under test. For instance if you had a method along the lines of:-

public void CreatePerson(string name, int age) {
    Person person = new Person(name, age);
    _personRepository.Add(person);
}

You might want to check the add method has been called on the repository

[Test]
public void Create_Person_Calls_Add_On_Repository () {
    Mock<IPersonRepository> mockRepository = new Mock<IPersonRepository>();
    PersonManager manager = new PersonManager(mockRepository.Object);
    manager.CreatePerson("Bob", 12);
    mockRepository.Verify(p => p.Add(It.IsAny<Person>()));
}

If you wanted to make this test more explicit you can use It.Is by supplying a predicate the person object must match

[Test]
public void Create_Person_Calls_Add_On_Repository () {
    Mock<IPersonRepository> mockRepository = new Mock<IPersonRepository>();
    PersonManager manager = new PersonManager(mockRepository.Object);
    manager.CreatePerson("Bob", 12);
    mockRepository.Verify(pr => pr.Add(It.Is<Person>(p => p.Age == 12)));
}

This way the test will through an exception if the person object that was used to call the add method didn't have the age property set to 12.

Times

If you had a method along the lines of:-

public void PayPensionContribution(Person person) {
    if (person.Age > 65 || person.Age < 18) return;
    //Do some complex logic
    _pensionService.Pay(500M);
}

One of the things that you might want to test is that the pay method does not get called when a person aged over 65 is passed into the method

[Test]
public void Someone_over_65_does_not_pay_a_pension_contribution() {
    Mock<IPensionService> mockPensionService = new Mock<IPensionService>();
    Person p = new Person("test", 66);
    PensionCalculator calc = new PensionCalculator(mockPensionService.Object);
    calc.PayPensionContribution(p);
    mockPensionService.Verify(ps => ps.Pay(It.IsAny<decimal>()), Times.Never());
}

Similarly it's possible to imagine situations where you're iterating over a collection and calling a method for each item in the collection and you'd like to make sure that it's been called a certain amount of times, other times you simply don't care.

SetupGet / SetupSet

What you need to be aware of with these guys is that they reflect how your code is interacting with the mock rather than how you're setting up the mock

public static void SetAuditProperties(IAuditable auditable) {
    auditable.ModifiedBy = Thread.CurrentPrincipal.Identity.Name;
}

In this case the code is setting the ModifiedBy property of the IAuditable instance while it's getting the Name property of the current instance of IPrincipal

[Test]
public void Accesses_Name_Of_Current_Principal_When_Setting_ModifiedBy() {
    Mock<IPrincipal> mockPrincipal = new Mock<IPrincipal>();
    Mock<IAuditable> mockAuditable = new Mock<IAuditable>();

    mockPrincipal.SetupGet(p => p.Identity.Name).Returns("test");

    Thread.CurrentPrincipal = mockPrincipal.Object;
    AuditManager.SetAuditProperties(mockAuditable.Object);

    mockPrincipal.VerifyGet(p => p.Identity.Name);
    mockAuditable.VerifySet(a => a.ModifiedBy = "test");
}

In this case we're setting up the name property on the mock of IPrincipal so it returns "test" when the getter is called on the Name property of Identity we're not setting the property itself.

SetupProperty / SetupAllProperties

Looking at the test above if it was changed to read

[Test]
public void Accesses_Name_Of_Current_Principal_When_Setting_ModifiedBy() {
    Mock<IPrincipal> mockPrincipal = new Mock<IPrincipal>();
    Mock<IAuditable> mockAuditable = new Mock<IAuditable>();
    mockPrincipal.SetupGet(p => p.Identity.Name).Returns("test");

    var auditable = mockAuditable.Object;

    Thread.CurrentPrincipal = mockPrincipal.Object;
    AuditManager.SetAuditProperties(auditable);

    Assert.AreEqual("test", auditable.ModifiedBy);
}

The test would fail. This is because the proxy created by Moq doesn't actually do anything in the set method of a property unless you tell it to. In affect the mock object looks a bit like this

public class AuditableMock : IAuditable {
     public string ModifiedBy { get { return null; } set { } }

}

To get the test to pass you have to tell Moq to setup the property to have the standard property behaviour. You can do this by calling SetupProperty and the mock will look more like

public class AuditableMock : IAuditable {
     public string ModifiedBy { get; set; }
}

and the test above would pass as the value "test" would now get stored against the mock. When mocking complex objects you might want to do this for all properties, hence the SetupAllProperties shortcut

Finally, the lightbulb in the IDE is the resharper plugin.

sighohwell
Excellent answer. Just what I needed as well.
nportelli
“You might want to check the add method has been called on the repository” – uh. Why would you want to check this? It’s *right there* in the code. Isn’t that the typical example of a redundant test that gives false confidence because it tests invariants that have already been established, thus leading to a selection bias?
Konrad Rudolph
It is a test that over specifies the behaviour of the code it's trying to describe, but it's also a test that demonstrates It.IsAny rather succinctly which is what the original question was about.
sighohwell
A: 

Thanks, it's very useful for my team, who are new to Moq.

Tiendq
Can you convert this into a comment with a +1 please? (I'm sure you dindt have the privilege at the time)
Ruben Bartelink
Surely, I have made +1.
Tiendq