Need Help?
 

If you're having trouble with an iFinity Product, use the Support Forums to search for answers, and to post questions.

If you need help faster than that, or can't figure out the answer, try our Premium Support service.

 
Crafty Code  
Feb 13

Written by: Bruce Chapman
Wednesday, February 13, 2008 2: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.

Copyright ©2008 Bruce Chapman

Tags:

Your name:
Title:
Comment:
Security Code
Enter the code shown above in the box below
Add Comment    Cancel  
   
Page Tags