codeflood logo

Unit Testing Sitecore Components Part 5: Recap and Resources

This is the final post in my "Unit Testing Sitecore Components" series. In this series I've taken a seemingly simplistic Sitecore component and refactored it by applying several principals to make the code more reusable and testable. In this final post of the series I'll recap the principals and provide a few resources to help explain them further.

This post is the final in a series covering the principals I showed during my virtual SUGCON presentation on unit testing Sitecore components in April of this year (2020). The following is a list of the posts belonging to the series, along with the principals covered in the post:

Unit Testing Sitecore Components Part 1: Logicless Views and Itemless models

  • Principal: Keep business logic out of the view.
  • Principal: Keep Item out of the model.

Unit Testing Sitecore Components Part 2: Encapsulate Logic

  • Principal: Encapsulate logic.
  • Principal: Use Dependency Injection and Abstractions

Unit Testing Sitecore Components Part 3: Avoid Static Members

  • Principal: Avoid implicit data
  • Principal: Use Sitecore Abstractions

Unit Testing Sitecore Components Part 4: Mocking Items and Fields

  • How to mock the Sitecore.Data.Items.Item class.
  • How to mock field values of a mocked item.

Principals Recap

This is a quick recap of the principals I've covered in this series. You'll find more detail for each in the posts listed above.

Keep business logic out of the views

Business logic burried inside a view can only be used inside that view. This is means the logic cannot be reused and generally makes unit testing quite difficult. To allow the business logic to be reused and properly unit tested, the logic must be kept in some other place outside of the view. Keep the view purely for presentation to transform from your model to the markup required for your channel (HTML).

Keep Item out of the model

With the APIs Sitecore offers and the prevelant use of the Item class, it may feel natural to use an item instance as the model in your project. Patterns such as the Custom Item Pattern also contribute to this confusion as custom items are a kind of model. The fact that the Item class is now easily mockable means unit testing is no longer a concern for this principal. But in production code, items cannot be instantiated directly. They must always be loaded from a database. Dependeing on how you use the item instances, this can cause performance issues at scale. It also means your model can never be provided from any other location other than a Sitecore database.

Encapsulate Logic

This principal is closely related to the "Keep business logic out of the views" principal above. To ensure logic can be reused, it must be encapsulated so it can be shared between components. Encapsulating logic also makes it easier to test the logic in isolation of any concerns of where it is used.

Use Dependency Injection and Abstractions

This principal possibly has the biggest impact on how complicated your unit tests will end up being. If a components dependencies cannot be specified at test-time, then those dependencies cannot be controlled and mocked. This principal ties back to the good old Object Oriented principal of "code to an abstraction" which specifies that classes should not contain references to concrete implementations, but only to abstractions. This makes changing the dependency much easier, whether that be a new dependency in production code or switching a real implementation to a test mock for testing purposes.

Avoid Implicit Data

Related to the above principal, make a classes dependencies explicit and clear. That will make the class much more reusable as the logic of the class is no longer tied to the implicit data and whatever it holds. Implicit data can also complicate tests, especially if that implicit data cannot be directly set.

Use Sitecore Abstractions

Sitecore contains a large number of static classes which provide access to various servies. These services are all now registered with the service provider as an appropriate abstraction. This allows code to follow the "Use Dependency Injection and Abstractions" principal and avoid making implicit service calls, which may fail in a test context. By using the appropriate Sitecore abstractions for things like manager classes, it makes it much easier to mock the class and inject it for tests.

Techniques Recap

The Unit Testing Sitecore Components Part 4: Mocking Items and Fields post of this series covered some techniques which rounded out the other posts and showed how to complete the unit tests for the sample component I was refactoring.

Mocking an Item

Ever since the Sitecore abstractions updates which were first introduced in Sitecore 8.2, mocking an item has been relatively easy.

Using NSubstitute to create mocks:

var db = Substitute.For<Database>();
var item = Substitute.For<Item>(ID.NewID, ItemData.Empty, db);

Mock Item Field Values

Fields can be mocked depending on how they're accessed in the calling code.

For simple item indexer access (assuming item is an NSubstitute mock):

item["field name"].Returns("field value");

Other field access approaches (like using the Fields property of the Item) are covered in the Unit Testing Sitecore Components Part 4: Mocking Items and Fields post.

Resources

I've created a repository on github containing the code I used in my presentation. You'll find it at https://github.com/adeneys/unit-testing-sitecore-components. The initial commit includes the component which I refactored during my presentation in it's initial (bad) state. I tagged each commit to explain the actions that were taken in that commit, so you can walk through the refactoring process.

To start at the first commit, after cloning the repository, simply check out the 0-start tag:

git checkout 0-start

Walk through the tags to watch how the code changes as the principals are applied:

git checkout 1-add-tests
git checkout 2-itemless-model
git checkout 3-controller-move-logic
git checkout 4-extract-logic
git checkout 5-refactor-controller
git checkout 6-refactor-view

The tags (and commits) don't match 1 to 1 with the principals covered in this series. This is because several princiapls were applied at once during a single logical change.

If you'd like to see exactly how I applied these principals during my SUGCON presentation, the good folks who organised the whole event managed to record the sessions and my full presentation is available on YouTube at https://www.youtube.com/watch?v=Gja_bAV15HM.

SUGCON Unit Testing Sitecore Components

Conclusion

In this series I wanted to emphasize that unit testing Sitecore components is more than just mocking items and fields. A little bit of design can both reduce the complexity of the test suite and allow more of the code to be reused.

Comments

Leave a comment

All fields are required.