Compartir vía


Behavioral Models

When you start using a framework such as Moles or TypeMock for unit testing, you may find that your unit tests often break when you change the way your logic is implemented. When your unit test dictates a specific response for each method call to a mock object, your unit test must reflect the state and behavior of the types that you are substituting. If you edit the code under test to use alternative methods, or even to call the same methods in a different order, you may find that your unit test no longer provides an accurate snapshot of the behavior of the dependency class—even though the functionality remains outwardly unchanged. Your unit tests become susceptible to frequent breaking changes, and risk becoming a reflection of the implementation details instead of a pure test of output conditions.

One approach to mitigating this problem is to implement behaved types that provide a more general representation of the class that you are faking. This allows you factor the behavior logic for dependency types out of your individual unit tests and into a single behaved type definition that you can reuse in multiple unit tests. For example, suppose you edit the way your code under test retrieves a list item—instead of using the GetItems method, you use the list indexer. Instead of updating every unit test to mock this new behavior, you would simply edit the behaved type for the list to ensure it supports the new retrieval method. If a behaved type doesn’t have the functionality required for your test, you can simply update the behaved type once and all future tests will benefit from the updated functionality.

Behaved types support the concept of state-based testing. In your unit tests, you assign values to the behaved type—for example, you might add a list item to a behaved type that represents a list. The behaved type will always return the same item, regardless of whether the code under test uses an indexer or a query to retrieve the item. This breaks the dependency between the overall functionality of the test code and the underlying implementation details of the test code. In other words, your unit tests simply set the state of the fake object, while the underlying behavior of the fake object is encapsulated within the behaved type. The use of behaved types leads to simpler and more resilient unit tests, and it's preferable to use behaved types instead of moles wherever possible.

The Moles installer includes many behaved type implementations for SharePoint and.NET Framework classes. The following example shows a test method that uses the behaved type implementations of the SPWeb, SPList, SPListItem, and SPField classes—BSPWeb, BSPList, BSPListItem, and BSPField respectively—which are provided by the Moles framework. The example tests the presenter class logic in a simple Web Part that implements the Model-View-Presenter (MVP) pattern.

[TestMethod]
[HostType("Moles")]
public void DoMagic_WithOneAnswer_ReturnsAnswer()
{
  //Arrange
  string answer = null;
  string error = null;  
   
  // First, set up a stub class to represent the view passed to the presenter. 
  var view = new SIMagicEightBallView();
  view.DisplayAnswerString = (s) => answer = s;
  view.DisplayErrorString = (e) => error = e;

  //Setup a behaved type for a web, add a list, and add an item to the list.
  BSPWeb web = new BSPWeb();
  BSPList list = web.Lists.SetOne();
  BSPListItem item = list.Items.SetOne();
  item.ID = 0;
  list.Title = MagicEightBallConstants.EightBallListName;
  item.Values.SetOne("answer.123");
   
  // add the field that will be used to the list fields.
  BSPField field = new BSPField();
  field.Id = MagicEightBallConstants.AnswerFieldId;
  list.Fields.SetOne(field);

  //Act
  var presenter = new MagicEightBallPresenter(view, web);
  presenter.DoMagic("Ask a question");

  //Assert
  Assert.IsTrue(answer != null);
  Assert.IsTrue(error == null);
  Assert.IsTrue(answer == "answer.123");
}

As you can see from the example, the use of behaved types simplifies the test method and makes it easier to read and understand. In many cases it also obviates the need for the developer to create moles for SharePoint types, which is beneficial as mole types can be complex to develop. The naming convention for a behaved type is to prefix the name of the class with the letter "B". This test performs the following actions:

  • It instantiates a behaved type to represent an SPWeb instance.
  • It creates a single list for the Web by calling the web.Lists.SetOne() method.
  • It adds a single item to the list by calling the list.Items.SetOne() method.
  • It assign values for the ID field, the Title field, and a custom answer field to the list item.

This allows us to test the logic of the presenter class—in short, we are able to verify that the presenter class returns the expected answer when we ask it a question.

It's worth taking time to explore some of the built-in behaved type implementations. Behaved types typically contain moles that define the functionality of the type. For example, the constructor of the BSPList class instantiate a new mole, of type MSPList, to represent the SPList class. Just like any other mole class implementation, the behaved type attaches several delegates to the mole class to define the behavior of particular methods. For more advanced scenarios, you can override the behavior of a behaved type by wrapping it with an additional mole. For example the following example overrides the behavior that the BSPWeb behaved type defines for the SPWeb.CurrentUser property getter.

BSPWeb web = new BSPWeb();    
MSPUser testUser = new MSPUser();
testUser.NameGet = () => "test name";
MSPWeb web1 = new MSPWeb((SPWeb)web);
web1.CurrentUserGet = () => testUser;

In general, you can build up the functionality in your behaved types progressively over time, as additional unit tests call for additional behavior definitions. However, you should avoid making the behaved type overly specialized or complex. If a particular unit test requires highly specialized behavior, you can still use the behaved type and override particular behaviors from within your unit test set up phase. For more information about using Pex and Moles to test SharePoint applications, including more details on behaved types, see Unit Testing SharePoint Services with Pex and Moles.

Note

The Moles Start Menu includes an option to build the behaved types using Visual Studio 2010. Selecting this option builds the behaved types to your user directory. You must perform this action before running the Moles-based tests provided with the SharePoint Guidance Library. The use of behaved types in the SharePoint Guidance Library is limited, as the Moles framework was still under development when the SharePoint Guidance Library components were implemented.