Introduction

If you're like me, you've tried two separate things and found that you like them. The two things I am talking about are:

building custom DotNetNuke modules, and

using the Unit Testing framework in Microsoft Visual Studio 2005.

However, like canned tuna and pineapple*, initially a lot of people might say that the two don't really go well together, but on further inspection will find that, with a bit of careful blending, they are indeed complimentary.

The main reason for unsuitability is the amount of UI-centric work that tends to soak into DotNetNuke PA Module development. But I think the introduction of unit testing into PA development is a good thing precisely for this very reason - a good unit testing framework in your development will make sure you don't glue the UI and Data layers too closely together. And using Test Driven Development (TDD) pays off, well, at least for me who likes to 'sketch' code - meaning trying a few different things and refactoring to get the finished product. My main focus is always driving the logic down away from the UI layer for reuse and good architecture.

This is not intended to be a posting on the merits of unit testing, understanding unit testing, or even how to create a unit testing project. It assumes the reader:

understands unit testing in Visual Studio and

understands DotNetNuke module development.

If you don't understand either of these topics, you'd be better off reading up on some of the many resources already published.

Note: This article is the first in two parts. The second part can be found at Using Data Driven Unit Testing with DotNetNuke. The second article shows how to use Data-Driven unit testing, but this article needs to be read first to understand.

TDD + DNN = GOOD

Test Driven Development is a good thing when it comes to quality improvements. Like many things, it feels counter intuitive and doesn't give immediate payoffs (in fact, it has an immediate cost when it comes to deliverables development time). But if you're into even mid-level complexity DotNetNuke Module development, you should be looking into it. Especially if you have Unit Testing ability in Visual Studio Team Edition. It provides you, as a developer, the ability to really get in and refactor your code quickly, reducing the need to waterfall-plan everything up front and get your design spot on. It's about reflecting the real world development process instead of a stuffy formalised version which I suspect 0 out of 100 DotNetNuke developments actually follow.. Who really knows what your module is going to look like when you finish writing it? A happy programmer hacking away can actually be more productive than a bored programmer filling out design documents.

In my case, I like to work bottom up, get the database design right, and build the application on top of that. So I generally start with the data structures and then define the access methods, then the UI code that runs on top of that. And this article concentrates on unit testing that part of the development - the definition and development of your Data Access code. If you want repeatable end-to-end testing of your create/update/delete (CRUD) code, you need a suite of unit tests. As is always the nature of unit testing, there is a level of hacking involved in getting it right, but put in the effort and you'll have a base-lined version of working code you can change at will without fear of breaking everything, and allowing you to get on with proper refactoring, instead of walking on glass worried what you might break. This is Test Driven Development at its best (in my opinion - your results may vary!). Just change the code, run the tests, and see what broke. Go back, fix it, re run the tests, repeat until you get the magical green ticks of a successful run of unit tests.

Just recently, whilst halfway through the development of a module, I decided to change the underlying database design. All I had to do was run the unit tests to check if everything still worked correctly afterwards. After a few code adjustments, I was on my way again. Without a battery of pre-written unit tests, I wouldn't have been sure if I had gotten it completely right, and possibly I would have persisted with the old (incorrect) design for fear of having to do too much rework.

When things run well, you get lots of 'green ticks'

This article naturally revolves around downloadable code, of course, but here's the background on how/why it works, and how to get your own DotNetNuke unit tests up and running. I'm going to assume that the reader has already learnt how to use Unit Testing projects in Visual Studio Team System - the rest isn't going to make much sense unless you have. If you, the reader, are familiar with NUnit rather than the VS2005 Unit Testing Framework, you can probably mod things a bit and get it working in the same way - there really isn't that much difference.

The main problems with running DotNetNuke unit tests are the fact that there are some assumptions in the Core DotNetNuke code that the library is going to be running in the context of a web application - which a unit testing application patently is not.

How to create a DotNetNuke Module Unit Test

Step 0: Download and extract the install package

You will use the libraries and files in steps 2 and 3. You don't need the code to run it, it's just there for the curious, or for people who want to extend it further.

Step 1: Create a Unit Test Project (or open an existing one)

To run DotNetNuke Unit tests, you need a unit testing project. This is easily done with the 'Add new project' menu item. You can either create it in a new solution or add the unit test project to an existing DotNetNuke solution you have.

Step 2: Create a folder for the DNN libraries

Create a \bin folder underneath the root of your unit testing project. This is for referencing the required files. I normally do it like this:

Locate the iFinity.Dnn.Utilities.dll library from the download and copy locally into your \bin folder. Copy all the latest DotNetNuke libraries into the \bin directory - you basically need to do a 'copy DotnetNuke*.dll'. You'll also need Microsoft.ApplicationBlocks.Data.dll. If you have the .pdb files for these DLLs, copy them into the directory as well; it helps with debugging when you can step into the core framework code.

The iFinity.Dnn.Utiltities.dll DLL requires the DotNetNuke libraries to be in the same directory. The download doesn't have a copy because most people run slightly different versions.

Step 3: Add in project references

Once you have copied in all the DLLs, use the 'Project-> Add reference' menu item, reference that file and some of the DotNetNuke libraries. These are the bare minimum libraries you need in your unit testing project:

DotNetNuke.dll

DotNetNuke.SqlDataProvider.dll

DotNetNuke.Caching.BroadcastPollingCachingProvider.dll

DotNetNuke.Caching.FileBasedCachingProvider.dll

DotNetNuke.Membership.DataProvider.dll

DotNetNuke.Membership.SqlDataProvider.dll

DotNetNuke.Provider.AspnetProvider.dll

Microsoft.ApplicationBlocks.Data.dll

YourNewModule.dll (the PA module you are working on - if you have the test project in your module solution, it's best to make this a project reference rather than a file reference).

This list really depends on what testing you are going to do within your unit test - and may need to be worked through until all the correct libraries are referenced.

Step 4: Create an app.config file

Create an app.config file in your unit testing project. You can create one of these with the Project-> Add New Item menu command, then choose 'Application configuration file' as the template type. Or just create an XML file and rename it app.config - either way you'll get it right.

From your DotNetNuke web.config file, copy in the sections for DotNetNuke which apply to connection strings, caching providers, and SqlDataProviders. The Visual Studio Unit Testing framework will automagically pick up and use the app.config settings, so when the DotNetNuke framework code looks for config settings such as caching providers and connection strings, it will take them from the app.config file instead of looking for a web.config file in the website directory.

You'll also need the 'iFinity' sectionGroup to run the supplied code. This is because the base class you are going to use needs some data on things to initialize. The easiest way to do all this is just to copy the app.config file from the downloadable code. It has all the right settings for a simple unit test application.

You may have to copy in more Provider settings depending on what providers you are accessing in the base framework. If you start poking around in the dark depths of the DotNetNuke libraries with your unit testing, don't be surprised to find you need to reference a provider that currently isn't in your app.config file. This will probably show up as a series of null reference exceptions or something similarly hard to find. The example provided covers most of them, but for people pushing the envelope or using custom providers, you might strike trouble.

Step 4: Create a new Unit Test

You're now ready to create a DotNetNuke unit test. Create a new unit test class and give it a name (and namespace).

In your UnitTest class file, you'll need to add in references to the correct libraries, so reference these with the 'Imports' (VB) or 'using' (C#) command.

Then change the declaration of the class so that it inherits from the DnnUnitTest type. The base class hooks up all the DotNetNuke handling in the background so you don't have to.

//C#
publicclass MyDotNetNukeClassTest : DnnUnitTest

VB

PublicClass MyDotNetNukeClassTest Inherits DnnUnitTest

There is a mandatory constructor on the underlying base class, so you'll need to create that. It requires a portal ID, so I normally just hardcode in whatever portalID I have on my test database I am using. You can always change it if you want to try another portal, or you can come up with a fancy solution to substitute in the portal ID if you need.

Step 6: Create a new test

Now create a unit test in your code, using the [TestMethod] notation as you normally would. Then simply write your unit test of your DotNetNuke code as you normally would for any other Unit Test. I normally just create my SqlDataProvider object directly in code and test it, rather than going through the indirect layer of creating the DataProvider class and using the Instance() call.

As you can see, my example test method simply saves a MyThing and then reads it back again, then compares the two properties to make sure they have the same value. You'll need to think about what aspects of your PA module code you'd like to test and write the unit test appropriately.

If you're lucky and have done everything right, you should just be able to run the unit test and get rewarded with a green tick. If not, well a bit of troubleshooting will be required to rid yourself of those pesky red crosses. You should be able to step right into your code and see where it is going wrong.

Troubleshooting

When things don't go so well, the dreaded red crosses

A common problem is not having your namespaces/assembly names right - it helps to ensure that you have your SqlDataProvider namespace and assembly name correct before trying to start unit testing. DotNetNuke uses a Reflection assembly that makes certain assumptions about naming standards so it's important to stick to these standards or spend hours picking through code working out why your data provider won't instantiate. You need to make sure your assembly name and namespace for the SqlDataProvider match up. An example for one of my projects is below:

This particular error is because the Caching provider is referenced in the app.config file but not in the project references. In fact, you'll be lucky if you can get one this clear to read.

When trying to get this to work, it's a good idea to turn on exception catching at the point it is thrown, so that you can capture exceptions right where they happen. Otherwise you'll end up with a lot of 'null reference exceptions' as objects fail to instantiate properly. You'll need to closely inspect the stack trace and exception stack - often the problem will be a not-found file deep down in the call chain, and takes persistence and detail-oriented work to get right.

Remember to set exception handling back to 'unhandled only' once you've solved your problem, or you will end up chasing down a lot of dark alleys where code has been written with the intention of using a try..catch block to handle normal operation (which is a performance no-no, but sometimes gets used). There is a bit of this in DotNetNuke, particularly in the CBO namespace (which is also why it should be avoided and replaced with directly-cast database routines, IMO).

Writing all of the required Unit Tests

Once you have got one unit test working, it's time to write the rest of them. Depending on what pattern you follow, you're likely to have at least four operations that need testing (the CRUD operations). Assuming a module called 'MyThings' with a MyThingInfo class, your DataProvider is going to have a list of methods that look something like this:

AddMyThing

UpdateMyThing

GetMyThing

DeleteMyThing

There should be a unit test written for each one of these (AddMyThingTest, UpdateMyThingTest, etc.). But don't forget unit tests are there for testing, so you'll need to use the GetMyThing call to get back your record after you save it. And then you should check that all of the properties have been returned properly. You also should test for things like adding in duplicate keys; what happens when you pass invalid data - not just the 'positive' test cases. And don't forget to delete your test data at the end of the test, unless you want your database clogged up with test data.

Of course, in practice I'm yet to create a DotNetNuke module with anything like the simplicity of this, but you never know, but it's good to illustrate the concept. The module I'm currently working on has about 25 different data access methods, each of which has one or two unit tests written against it.

How does it work?

The underlying difficulty with mixing unit testing and DotNetNuke, as mentioned earlier, was the assumption that the code will be running inside a web application. The real tricky bits come in where the HttpContext is used to cache items, as well as the various DotNetNuke caching providers. Now the DotNetNuke code is robust enough to create objects when they aren't found in the cache and guarantee you an instantiated object when you ask for one, but it does expect the cache to be there in the first place. Without a web app, you've got not HttpContext, and you're stuck.

Are you mocking me?

Mocking is the process of mocking backend functionality that is not present when unit testing. Also called 'stubs' and other names, it basically means providing something for the code to call when it is running in the Test context rather than the full DotNetNuke app. It's not going to be long before you make a call to something like HttpContext and find that the Unit testing context isn't running such an animal. So you need to mock some of the things that the DotNetNuke takes for granted. We can get into a discussion of design architectures and separation of libraries vs. websites at another time. I never criticise the framework because it's done voluntarily and is distributed free.

I've implemented a version of the method used in the first link. This provides the DotNetNuke library with the right objects for it to run.

For those too lazy to RTA, what it does is set up a simple object that mocks some of the HttpContext functions. Not all, so you won't have to push it very far to break it (it won't support the vast majority of calls to HttpContext). But you're building class libraries! What are you doing assuming there will be an HTTP Context anyway?

Where can I put my stuff?

DotNetNuke heavily uses caching to store all sorts of things during runtime. Some of these are the objects for the providers (depending on whether you use disk caching or not). And without somewhere to stash all those instantiated providers, you're going to get exceptions all over the place. So you need to make sure you have your Cache paths set up. No, there isn't a magic 'SetMyCachePathAndMakeMyProblemsGoAway' method you can call. You have to craft up a way to trick the DotNetNuke library into thinking it's actually running as normal. To do this, a few 'startup' variables have to be set.

In my base class, these paths get set to values which are specified in the app.config file. I don't really like this approach much myself, because it means that any other machine running your unit tests (a build machine, for instance) needs to have the same paths specified. I didn't have much luck getting relative paths to work well, although I'm willing to concede it's probably not that hard.

If you look at the iFinity/DnnUnitTest settings, you'll see a couple of paths specified:

hostMapPath: This is where cached data etc., gets placed when the application is running normally. In this case, I have the path set to where my local DotNetNuke install exists.

appPath: The root path of where the website normally runs. Again, I have this set to my local install.

simulatedServer: As far as I can tell, this value doesn't mean a hell of a lot, but it needs to be specified. I use my local machine name; "localhost" would probably work as well.

simulatedPage: The name of the ASP.NET page you are simulating. Because all DotNetNuke pages run as default.aspx, this one is pretty easy.

Note: You don't have to set these to the same value as a local DotNetNuke install, and you don't really have to have a whole DotNetNuke website installed on the computer you are using. As long as they are a valid path, everything will be fine. I just use my local DotNetNuke install because the folders are already created with the correct permissions.

How far can this be pushed?

Eventually, you'll end up with either half the DotNetNuke website loaded up trying to unit test, or you'll find yourself trying frantically to mock something that can't and won't be mocked. However, with some creative coding and delving into the core source code, you'll find that most problems can be overcome. Then you'll have a reliable set of unit tests that can be run every time you make a change to the code. Any breakages will quickly be found and you'll feel confident in doing some real refactoring when circumstances require it.

Not only that, but by the time you get around to writing your UI code in your ASCX file, you'll be supremely confident that your data layer code works exactly as you intended. It's a major pain to debug web applications, so debugging it before you hook it into a web app is much easier.

Did it work?

The code started out as a helper I continually refined until it did what I wanted, and this article started out as my 'cheat sheet' to help me troubleshoot faster. It has finally morphed into something for public consumption, but there's probably cryptic instructions and problems with it that I haven't picked up. If you use it and have trouble using it or don't understand it, use the comments field below to ask questions and I'll attempt to clear up any difficulties with it.

* Whilst studying at university, I briefly shared a house with a Scottish body builder who worked as a bouncer. He ate tuna and pineapple for lunch every day as it was high in protein and sugars and low in fat. Oh, and it cost about $1 for the two cans. It's the mixture of salty and sweet tastes, dry and juicy textures that sets it off. Far better than noodles (a.k.a. Ramen) because you don't have to cook it, and with more nutritional value.

Comments and Discussions

Perfect tool that makes my life and TDD with DNN much easier but I have been battling to make it work for few days. I have DNN 5.1 so I am not sure if it has to do with using newer version(need to try with older to see if error goes away) . I have see update Bruce provided for later versions of DNN but the error I get is different so I am not sure if it is just something wrong I do or it change in DNN 5.X later versions.

Here is what I get on the line of constructor : public MyDotNetNukeClassTest() : base (1)

DnnUnitTesting.MyDotNetNukeClassTest (TestFixtureSetUp):
System.InvalidOperationException : Cannot register or retrieve components until ComponentFactory.Container is set

at DotNetNuke.ComponentModel.ComponentFactory.VerifyContainer()
at DotNetNuke.ComponentModel.ComponentFactory.GetComponent[TContract]()
at DotNetNuke.Common.Utilities.DataCache.GetCache(String CacheKey)
at DotNetNuke.Common.Utilities.DataCache.GetCachedData[TObject](CacheItemArgs cacheItemArgs, CacheItemExpiredCallback cacheItemExpired)
at DotNetNuke.Entities.Portals.PortalController.GetPortal(Int32 PortalId)
at DotNetNuke.Entities.Portals.PortalSettings..ctor(Int32 tabID, PortalAliasInfo objPortalAliasInfo)
at iFinity.DNN.Utilities.Testing.DnnUnitTest..ctor(Int32 portalID) in C:\Development\PER\DNN_Stuff\DnnUnitTest\DnnUnitTesting\iFinity.DnnUtilities\DnnUnitTest.cs:line 58
at DnnUnitTesting.MyDotNetNukeClassTest..ctor() in C:\Development\PER\DNN_Stuff\DnnUnitTest\DnnUnitTesting\DnnUnitTesting\MyDotNetNukeClassTest.cs:line 17

Googeling it brought me on similar problem with DNN when web.config had some errors but I double checked my and everything is ok.

Thank you for the superb article! This is exactly what I was looking for!

However, I was not able to verify the described functionality. I am testing your unit testing module on a fresh 4.8.2 instance and get TypeInitializationException. I am pretty sure that I configured the application right according to the description in the article (both references and app.config), but still...

I'm glad you've found it useful. The error you're encountering is as a result of changes to the DNN framework since when I wrote this and the latest version. It's to do with the way the mock host loads up the types from the different providers in DNN, now that they (DNN Core Team) have reduced the number of assemblies. I spent a lot of time trying to figure it out, so you've done the smart thing and just asked!

I haven't posted a download with the updated code in it as yet, but if you add the code on the blog post into the 'DnnUnitTestConstructor' code, just before the call to 'LoadDNNProvidres', you should be able to get yourself going - use the supplied list of providers to pre-load.

It will take me a while to package up an updated version of the code - I'm always tinkering with it and I have to be sure it is solid before releasing, otherwise I end up answering emails all day

However, check your app.config settings for the 'hostMapPath' and 'appPath' settings. It's likely that when you are creating the 'PortalSettings' object that you have a blank value or similar - hence the regex failure.

In my testing, I run app.config settings like this:
hostMapPath="Website\portals\0\" appPath="Website\"
..and, as a solution file, I create a batch file (TestSetupScript.cmd) which has the following commands in it:
MD Website
MD Website\Portals\0
MD Website\Portals\0\Cache

In my localtestrun.testrunconfig file, I specify 'testSetupScript.cmd' as the 'setup script'. This ensures that the correct website\portals\0 path is created relative to the test directory output path.

If you've already done these steps, let me know. You may have to load up the dnn library code into your solution and step through it in testing to see what the failure is.

Try creating a 'testSetup.cmd' file as I replied in the previous post, then specifying this in the command line for 'test setup' in your solution items. This will create the relevant path, which (might) help with the error.

FYI, VS 2008 (currently running Team Edition) allows you to set the test config. to run within a web process, just set it to host and the correct http address and unit testing worked as is with no additional setup. I was able to call the data access routines like normal.

The value for HostPath needs to be refactored into the configuration class, but a value is apparently needed. I'm not sure how my DNN site differs from everyone else's who doesn't need these settings. FWIW, I'm using DNN v4.8.3.

Presently, I've gone no further than to instantiate my test fixture, which inherits DnnUnitTest, but the instantiation works now.

I use a mixture of what I've seen other people do, the 'official' module developers guide and my own experience.

However, the official module developers guide has kind of swung towards non-PA development, so I've stuck with the old ways of doing it.

Basically I do this:
- one Assembly with all the business entities, controllers, data provider and user controls
- one Assembly for the SqlDataProvider

The focus of this article was mainly with SqlDataProvider (because that's where a lot of in/out data processing takes place. The simpler things like checking the logic on your MyThingInfo classes doesn't need any special setup - you can just unit test them like anything else. And you can't really unit test the userControls. At a pinch, if you write it in the correct way, you can test the logic in your 'Controller' class.

Something I just noticed (literally 15 minutes ago) was that the line I wrote about the SqlDataProvider dll being named 'yourcompany.Modules.yourmodule.SqlDataProvider' depends on what you put into the constructor of your DataProvider class.

In my case, the constructor I use (which changes only in module name) is this:

The above line of code asks the DotNetNuke Reflection class to return an instance of the type. It does this by sticking 'SqlDataProvider.dll' on the end of the assembly name. The 'SqlDataProvider' comes from the type name of the derived type being instanced. Ie, SqlDataProvider derives from DataProvider. If you created a Provider class called 'AccessDataProvider' which derived from DataProvider, it would look for 'yourcompany.Modules.yourmodule.AccessDataPRovider.dll'.

I realise that's an answer to a question you didnt' even ask (surprisingly, I'm not a politician!)

Hi Bruce,
Thanx for the article, i just implemented your suggestions in the help project. Worked a treat. the only thing I had to change was to separate the sql provider dll into its own dll again.
Are you going to Tech Ed on the Gold Coast, if you are look me up, I work for Readify and we have a booth there, leave me a message and we'll catch up.
Cheers

I actually wrote a blog on the HTTPContext topic over a year ago at dotnetnuke.com[^]

Like many things on the project, unit testing is one of those items that has been on our list of things to do for a long time. Since none of us have a long track record of doing automated unit testing, and given that there are always 1000 other tasks vying for our time, it has always been placed low on our list of priorities. I think you will see this change this next year as we work to improve our overall testing methodology. And like every project that begins unit testing, you can expect to see DNN evolve over time to make it easier to unit test (trust me, we don't want to make this any harder on ourselves than need be).

Thanks for posting the article. This should make it much easier to get the necessary team members up to speed on unit testing and will go a long way towards getting this process kicked off.

I haven't done a lot of work with the scheduler, so I hadn't come across the HttpContext error.

It's probably a bit late to remove the assumption that HttpContext will be there in the application, but that type of thing tends to create a lot of problems over the long term as newer programmers come on board, inspect the object viewer and set off on their task.

I've been using this unit testing methodology for a couple of projects now, I don't have 'hard' data on whether or not it saves time, but it certainly gives better confidence that you can see how much you have impacted the code base with changes at the lower data levels. I also use data driven unit testing, which I've just posted a further article to this one at http://www.codeproject.com/useritems/DnnDataDrivenUnitTesting.asp[^].