DNN Development Tips:9 - Using Moq, the “It” class

Tags: testing moq It
Last Modified: May 2 2017
Oct 22 2014

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:

  1. Is(Expression>)
  2. IsAny
  3. IsInRange(TValue from, TValue to, Range rangeKind)
  4. 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 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.

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    Tags