Unit Testing for Kentico Objects

Written by Jon Gregory
13th October 2016

8 minute read

One of the big difficulties I have found when working on bespoke code for a Kentico based website was writing unit tests where the code under test used Kentico objects. For example, the Kentico AddressInfo object being used in a custom code method.

When writing the test an AddressInfo object would need to be created and passed in as a parameter by the test. However the creation of the object would fail without a connection string even though the database is not required.

I have seen a few approaches where people have written wrappers around the providers classes but if you want to work with the Kentico Objects they still had the inability to create a new object.

Starting in Kentico 8, there is a hidden gem library called CMS.Tests, which provides the solution to the problem. There is not a great deal of documentation on this but this Kentico Article covers everything. There is also a Class Reference document which shows all the methods and properties.

There are three test types available:
  • Unit Tests
  • Integration Test
  • Isolated Integration Tests

Unit Tests

As you would expect these isolate the code from the database and allow for the tests to run quickly with the provided fake data.

The key thing to do is make sure the tests inherit from the UnitTests base class in CMS.Tests otherwise the Fake<> methods cannot be used.
 

C#
[TestClass]
public class IamAUnitTest : UnitTests
{
.....


Once that is set up it is a simple case of creating Fake<> for each Kentico Object and its provider that the test requires...
 

C#
Fake()
	.WithData(new AddressInfo()
	{
		AddressGUID = _billingAddressGuid,
		AddressLine1 = "AddressLine1",
		AddressLine2 = "AddressLine2",
		AddressCity = "AddressCity",
		AddressZip = "Postcode"
	},
	new AddressInfo()
		{
			AddressGUID = _deliveryAddressGuid,
			AddressLine1 = "AddressLine1",
			AddressLine2 = "AddressLine2",
			AddressCity = "AddressCity",
			AddressZip = "Postcode"
		});

You can provide as many objects as you want on the .WithData() method, and then the fake provider will let you look up on ids providing realistic scenarios. This has been really useful when testing the logic in custom methods without having to spin up the website and initiate through the UI. It is also great for edge case and exception testing to see how logic performs when you get something unexpected back from Kentico. There is a full list of the fakeable Kentico Objects here.

Integration Tests

These allow the code to be run against the database, but don't require a Kentico website. As long as all the required Kentico Binaries are referenced and an App.Config file with the database connection string. These will be slower than a Unit test but allow for more realistic scenarios. Care should be taken if the tests write data to the database as this will be harder to reset at the end of the test, and could effect database integrity.

As above, the tests have to inherit from the IntegrationTests base class, but there is no need to call the Fake<> method as these tests will be accessing the database.
 

C#
[TestClass]
public class IamAIntegrationTest : IntegrationTests
{
.....
 

Isolated Integration Tests

These tests are similar to the above integration tests, except it creates a copy of the database using SQL Server 2012 Express LocalDB to protect from any database writes affecting the integrity of the data. The local database gets refreshed with each test so these will be slow to run, they are ideal for longer integration tests, perhaps on a nightly build.

The logic can be tested against a clean seeded database for each test which will be repeatable on each run. CMS Asserts There is also a CMS Assert Methods set of classes that allow Kentico Specific assertions, these can be used alongside the Test framework assertion provider. An example assertion taken from the Kentico article is the QueryEquals method, which checks whether two SQL statements are equal.
 

C#
CMSAssert.QueryEquals(q.ToString(), "SELECT UserID FROM CMS_User");