iFinity Blogs 

Bludgeoning DotNetNuke Unit Testing into life

by Bruce Chapman on Wednesday, February 13, 2008 3:38 PM
My previous work in this area was centered around the seemingly simple task of writing unit tests for DotNetNuke-hosted business objects, so that the testing of the simple database and logic operations of modules could be tested automatically. However much of the DotNetNuke core is built with the underlying assumption that it will be running in the context of an ASP.NET application. That's fair enough, it is a Web Application Framework so you'd expect that.

However, if you want to unit test, you won't be running in the context of ASP.NET, you'll be running in the context of the testing tool you are using (for me, Visual Studio Testing).

I got it to work fine by writing a base class which handled all the plumbing to get the minimum objects for DotNetNuke up and running, and by hacking up the code supplied by Phil Haack. Specifically, this centres around key objects like host settings, portal settings, the cache and the various data providers. It worked sweetly, and I've run hundreds if not thousands of unit tests using my little helper class.

But along came DNN 480, and for my latest module project, I decided to write it against 4.8.0 and worry about any backwards compatibility if the issue arose (in reality when someone complains). There was a change (about 4.6, I think) in which all the *.Data.SqlDataProvider.dll provider files disappeared from the DNN core. I've never taken the time to investigate exactly what went on there, but I assume they were rolled up into a single assembly. So far so good.

However, upon running my tried-and-true DotNetNuke http simulator class with some new unit tests, I kept getting the failure on the instantiation of certain provider types- yes, you guessed it, the SqlDataProvider types. I proceeded to tear hair out, give the google servers a jolly good flogging and all round swear out loud. I isolated the problem to this line of code in Reflection.vb (line 279)

' use reflection to get the type of the class
objType = BuildManager.GetType(TypeName, True, True)

When trying to load up the base SqlDataProvider (DotNetNuke.Data.SqlDataProvider), the BuildManager.GetType call would look in the System.Web assembly for the type. And predictably come up empty handed and throw a snooty 'type exception' back at me. So I stepped through with a DNN install calling the code - same deal, BuildManager.GetType called with the type 'DotNetNuke.Data.SqlDataProvider' (note, the assembly isn't specified). Running as a DNN website though, the code executes flawlessly and the System.Web.Compilation DLL (where BuildManager.GetType lives) finds the correct DotNetNuke.DataProvider.dll assembly and everything runs smoothly.

I must have investigated every setting, config option and double checked every file before I came to the conclusion : Black Magic is afoot on my processor. No, seriously, there's something fundamentally different about running in my mocked up Http context in Unit Testing as opposed to the 'proper' ASP.NET context. And whatever is different affects the BuidlManager.GetType call in a way that I have lost interest in finding out.

So I did the next best thing - I started rehacking my frankencode Unit Testing helper class, and I just forced all the types for the required providers to load. This works, because with a call to Reflection.CreateType() can specify the cache key. If you load and cache the types before calling a line of DNN code, the objects are already in the cache and the black magic demons which prevent BuildManager.GetType() from finding the right assembly are cut off at the pass.

Now I don't recommend anyone actually include this code in a live project that people will depend upon, but FWIW, here's how I did it :

private void CacheTypes(string cacheTypeList)
{
if (cacheTypeList != null)
{
string[] cacheTypes = cacheTypeList.Split(';');
foreach (string cacheType in cacheTypes)
{
if (cacheType != null && cacheType.Length > 0)
{
string[] parts = cacheType.Split(':');
string type = parts[0];
string cacheKey = parts[1];
object typeObject = DotNetNuke.Framework.Reflection.CreateType(type, cacheKey, true, true);
if (typeObject == null)
Console.WriteLine("Type failed on pre-load: " + type);
else
Console.WriteLine("Type load succeeded : " + type);
}
}
}
}


All you need to do is call the code with a delimited list of types and cache key names, like this:
(note, this list has two delimiters- the ':' which separates the type from the cache key, and the ';' which separates types that have to be loaded.

DotNetNuke.Security.Membership.Data.SqlDataProvider,
DotNetNuke.Membership.Dataprovider:DotNetNuke.Security.Membership.Data.SqlDataProvider;
DotNetNuke.Data.SqlDataProvider,
DotNetNuke.SqlDataProvider:DotNetNuke.Data.SqlDataProvider

(also note this is broken across lines for brevity)
If you're further interested in Unit Testing DNN modules, just join the Support Forum and start a thread about DNN Unit testing.

Blogs Parent Separator Crafty Code
Author
Bruce Chapman

The craft of writing code. The outcomes from being crafty with code. Crafty Code is tales from the coding bench.

1 comment(s) so far...

Anonymous 4/13/2009

Hi, I use the unit test program posted on 'Unit Testing a DotNetNuke Private Assembly Module" at CodeProject and met the DataProvider type initial problem. I jumped to this article from the answer of you in the comment.<br>I try to add the CacheTypes function into the DnnUnitTest.cs, but the last couple of lines in this article confused me, Do I need to include all four lines of this:<br>DotNetNuke.Security.Membership.Data.SqlDataProvider,<br>DotNetNuke.Membership.Dataprovider:DotNetNuke.Security.Membership.Data.SqlDataProvider;<br>DotNetNuke.Data.SqlDataProvider,<br>DotNetNuke.SqlDataProvider:DotNetNuke.Data.SqlDataProvider<br>into the paramentes of CacheTypes function?

Bruce Chapman
Hi, I'm Bruce Chapman, and this is my blog. You'll find lots of information here - my thoughts about business and the internet, technical information, things I'm working on and the odd strange post or two.
Connect with Bruce Chapman on Google+

Share this page
Get more!
Subscribe to the Mailing List
Email Address:
First Name:
Last Name:
You will be sent a confirmation upon subscription

Follow me on Twitter
Stack Exchange
profile for Bruce Chapman at Stack Overflow, Q&A for professional and enthusiast programmers
Klout Profile

Page Tags