This article is cross-posted from my personal blog.
In a previous article in this series of blog posts, I introduced Moq (Mock-you) – the mocking framework we are using in DotNetNuke to generate Mock objects for testing.
In this article I will start to dive deeper into this framework by looking at the unusually named “It” class.
What is “It”?
“It” is a static helper class that provides 4 static methods that allows testers to match a method invocation with an arbitrary value, with a value in a specified range, or even one that matches a given predicate.
The four methods it provides are:
- Is(Expression
>)
- IsAny
- IsInRange(TValue from, TValue to, Range rangeKind)
- IsRegex(string regex) (+ overloads)
The name of the class, while it appears strange, allows us to write readable tests.
For example, continuing our use of the VocabularyController class, lets look at a test for the AddVocabulary method. Listing 1 shows the AddVocabulary method.
Listing 1: The AddVocabulary method of VocabularyController |
1: Public Function AddVocabulary(ByVal vocabulary As Vocabulary) As Integer _
2: Implements IVocabularyController.AddVocabulary
3: 'Argument Contract
4: Requires.NotNull("vocabulary", vocabulary)
5: Requires.PropertyNotNullOrEmpty("vocabulary", "Name", vocabulary.Name)
6: Requires.PropertyNotNegative("vocabulary", "ScopeTypeId", vocabulary.ScopeTypeId)
7:
8: vocabulary.VocabularyId = _DataService.AddVocabulary(vocabulary, _
9: UserController.GetCurrentUserInfo.UserID)
10:
11: 'Refresh Cache
12: DataCache.RemoveCache(_CacheKey)
13:
14: Return vocabulary.VocabularyId
15: End Function
|
As in the previous article, one of the tests we need to write is a test that confirms that the VocabularyId property of the vocabulary is set to the value returned from the call to the DataService (line 8).
Listing 2: Testing that the VocabularyController’s AddVocabulary method sets the VocabularyId correctly.
|
1: [Test]
2: public void VocabularyController_AddVocabulary_Sets_ValidId_On_Valid_Vocabulary()
3: {
4: //Arrange
5: Mock mockDataService = new Mock();
6: mockDataService.Setup(ds => ds.AddVocabulary(It.IsAny(), It.IsAny<int>()))
7: .Returns(Constants.VOCABULARY_AddVocabularyId);
8:
9: VocabularyController vocabularyController = new VocabularyController(mockDataService.Object);
10:
11: Vocabulary vocabulary = ContentTestHelper.CreateValidVocabulary();
12:
13: //Act
14: vocabularyController.AddVocabulary(vocabulary);
15:
16: //Assert
17: Assert.AreEqual<int>(Constants.VOCABULARY_AddVocabularyId, vocabulary.VocabularyId);
18: }
|
In this example we make use of the “It” class in the Setup method of the Mock. This code is pretty self-explanatory. In the set up, as long as the mock DataService’s AddVocabulary method is given any Vocabulary instance (It.IsAny()) and any integer (It.IsAny()) it should return a known VocabularyId (Constants.VOCABULARY.AddVocabularyId).
The beauty of this class is it is clear what the methods are being used for.
It.IsAny() – means accept any instance of IFoo
It.Is(i => i%2 == 0) – means accept any even number (or any integer which is divisible by 2)
For example:
// given any value return true
mock.Setup(foo => foo.Execute(It.IsAny<string>())).Returns(true);
// given any even number return true
mock.Setup(foo => foo.Add(It.Is<int>(i => i % 2 == 0))).Returns(true);
// If the value is between 1 and 10 return true
mock.Setup(foo => foo.Add(It.IsInRange<int>(0, 10, Range.Inclusive))).Returns(true);
// If the string matches the Regex [a-d]+, return "foo"
mock.Setup(x => x.Execute(It.IsRegex("[a-d]+", RegexOptions.IgnoreCase))).Returns("foo");
We can also use it in the Assert phase to verify that a method was called with a particular value..
// assert Execute was called - with any string
mock.Verify(foo => foo.Execute(It.IsAny<string>()));
// assert Add was called with an even number
mock.Verify(foo => foo.Add(It.Is<int>(i => i % 2 == 0)));
// assert Add was called with a value between 1 and 10 return
mock.Verify(foo => foo.Add(It.IsInRange<int>(0, 10, Range.Inclusive)));
The “It” class provides readable matching conditions and is an important part of the Moq framework. In the next part of this series I will continue to dive deeper into this awesome mocking framework.