An Assembly History of NDbUnit
As some of you are aware, I have some time ago assumed the role of primary committer on the opensource NDbUnit project. The history of this project is that it began with just two database ‘target’ types supported: Microsoft SQL Server and Microsoft OLEDB-supported databases.
All of this capability was wrapped up into the single assembly, NDbUnit.Core.dll that had hard binary dependencies only on standard Base Class Library assemblies that shipped with the .NET framework (System.Data.dll that contains the ADO.NET client implementations for SQLServer and OLEDB in this case). Since a developer had to have the .NET Framework installed to be writing .NET software in the first place, this effectively meant that NDbUnit.Core.dll had a hard dependency only on other binaries that all adopters would already have on their computers.
Essentially it meant that all developers using NDbUnit would use the same assembly references regardless of what database ‘target’ they were working with.
Growing to Support Third-Party (non-MS) Databases
Due largely to the contributions of others, the NDbUnit project slowly grew to work with and support additional, non-Microsoft database targets. Today, in addition to the support for MSSQLServer and MSOLEDB, NDbUnit also supports the following third-party databases:
- SQL Server CE
I include ‘SQL Server CE’ here even though its (obviously) also a Microsoft database because it turns out that if you install Visual Studio and fail to check the box to include ‘mobile development support’, you don’t get the needed System.Data.SqlServerCe.dll assembly installed on your PC.
The trick will all of these third-party database targets is that they all require some third-party assembly in order for ADO.NET to interact with them. And since NDbUnit is leveraging ADO.NET under the covers to interact with your database, NDbUnit needs these assemblies in order to work properly…
- MySQL (MySql.Data.dll)
- SQLite (System.Data.SQLite.dll)
- SQL Server CE (System.Data.SqlServerCe.dll)
The Trouble Begins…
The real issue at the core — no pun intended — of this trouble is that as originally conceived (and thus as was the case when I assumed ‘ownership’ of the codebase), all of the supported database targets were compiled into the single NDbUnit.Core.dll assembly. When the only supported databases were SQL Server and OLEDB, this didn’t matter too much. But as the number of supported databases grew and expanded to include non-Microsoft targets, the fact that all of this code was compiled into the single NDbUnit.Core.dll assembly meant that anyone wanting to use NDbUnit for one database target was required to take a dependency on all the database targets. This results in the ungainly dependency graph shown here…
…and it effectively means that if you reference in the NDbUnit.Core.dll assembly, you also have to reference in all of the other third-party assemblies whether you want / need to use them or not.
Clearly not a very good ‘take just what you need’ story for NDbUnit. Instead, the story became ‘all or nothing’ and for a project with a wide-variety of adopters intended to be useful in a wide variety of situations, clearly this was becoming a real problem.
And worse, its a problem that will expand as other database targets are added to the project (e.g., Oracle support is under construction right now) — any new database target support assemblies would need to be referenced by any and all NDbUnit users right along with NDbUnit.Core.dll.
…and Leads to a Fix!
The solution I have now introduced into the NDbUnit project (which frankly isn’t really all that ground-breaking) is to divide the dependencies into more database-specific assemblies.
Where previously all of the support for each different database target was incorporated directly into the single, monolithic NDbUnit.Core.dll assembly, now the core assembly merely contains the common interfaces and base classes that each of the database target-implementation classes depend upon. The database-target-specific code has been moved into separate assemblies, one each for each of the supported database targets as follows:
|Database Target||NDbUnit Client Assembly|
|MS SQL Server||NDbUnit.SqlClient.dll|
|MS SQL Server CE||NDbUnit.SqlServerCe.dll|
Each of these NDbUnit client assemblies in turn depends on NDbUnit.Core.dll and exactly ONE (and only ONE!) of the other ADO.NET-supporting assemblies.
As an example, the following dependency diagram shows the assemblies needed for support of MySQL using NDbUnit. Your test project needs only to reference the MySQL-specific NDbUnit client assembly (NDbUnit.MySql.dll), the common assembly NDbUnit.Core.dll, and the MySQL-provided ADO.NET support assembly, MySQL.Data.dll.
While the effect of this is that the NDbUnit-adopter has more separate assembly dependencies to manage when setting up their test projects, the benefit of this approach is that adopters of NDbUnit are now only required to take a binary dependency on the libraries that are needed to support just the database target that they are interested in working with rather than a dependency on all of the assemblies needed to support every conceivable database target that NDbUnit supports now and for the future.
Please also note that there is NO change in any of the namespaces with any of the types in NDbUnit that are being introduced with this refactoring, so all that is needed to update any of your projects to use this new assembly distribution model is to download the latest binaries (or get the code and build it for yourself), change your project’s references to whatever is appropriate for your database target, and recompile. Your tests should still ‘just run’ as before.
Another Positive Side-Effect
One other side-benefit of this change in approach is that it now permits the versioning of the assemblies completely separate from each other. A change in the NDbUnit MySQL client assembly won’t result in a (somewhat needless) version change for anyone NOT using MySQL (for example) and users can only bother to update their versions when changes are introduced in the libraries that support their specific database targets.
This will also help when introducing support for multiple DB target versions from the same vendor (e.g., Oracle 8, 9, 10, 11, whatever) in that each client support assembly can be considered a separate assembly and chosen by users as needed for their version of the database target.
Happy database testing~!
At the moment I am enyoing your great screencast Summer Of NHibernate. In session three you are doing some unittesting and for that you use a dll from Microsdesk. Since I am using a MySQl database I wanted to use the Proteus library for unittesting.
So, as I found on this page, I added a reference to NDBUnit.MySQL.dll, NDbUnit.Core.dll and MySQL.Data.dll. I then created the methods the way you do in your screencast (including the dataset).
If I then run the method “GetMyTestDataXMLFile” I recieve an error. See below:
failed: Can’t load assembly NDbUnit.SqlClient; ensure the assembly is located in the app binary directory or the GAC
Why does it want to load the NDbUnit.SqlClient? Do you have any idea? I alse have the MbUnit.framework as a reference for the earlier tests.
Thanks in advance.
Assuming that you have the proper version of the libraries referenced, there should be no explicit dependency on NDbUnit.SqlClient.dll (so I cannot immediately see the reason for your error).
If you are using the Proteus libraries from the Google Code site, can you please ensure that you are also referencing the NDbUnit.* assemblies that are in the Google Code download of the latest Proteus binaries ( http://proteusproject.googlecode.com/files/Proteus.Utility.UnitTest_184.108.40.206419.zip ) rather than either the older NDbUnit binaries from the screencast code downloads or the newer NDbUnit binaries from the NDbUnit Google Code site?
Note that if you are applying these to an existing solution, you MUST do a CLEAN of the solution and then a complete REBUILD of the solution after updating the references as Visual Studio doesn’t (always) consider a change in the version of a referenced binary to be a reason to rebuild a project and so its possible you have ‘stale’ / ‘old’ binaries in your bin/Debug folder(s).
If you try this and it still doesn’t work please try to get me a copy of the entire solution that evidences the problem and post it somewhere that I can get ahold of it (either post a ZIP file somewhere I can download or check the whole solution (binaries and all) into one of the free SVN hosts out there.
Let me know how you make out. The behavior you’re reporting should certainly NOT be true if you have the proper versions of the binaries referenced in your solution.