DNN Development Tips:9 - Using Moq, the “It” class
In a previous article in this series of blog posts, I introduced Moq (Mock-you) – the mocking framework we are using in DNN 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 int AddVocabulary(Vocabulary vocabulary) 2: { 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, UserController.Instance.GetCurrentUserInfo().UserID); 9: 10: //Refresh Cache 11: DataCache.RemoveCache(_CacheKey); 12: 13: return vocabulary.VocabularyId; 14: } |
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 6: mockDataService.Setup(ds => ds.AddVocabulary(It.IsAny 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
The beauty of this class is it is clear what the methods are being used for.
It.IsAny
It.Is
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.