{"id":115,"date":"2007-11-04T19:08:04","date_gmt":"2007-11-05T02:08:04","guid":{"rendered":"http:\/\/jameskovacs.com\/2007\/11\/05\/Testing+Immutable+Entities+With+NHibernate"},"modified":"2007-11-04T19:08:04","modified_gmt":"2007-11-05T02:08:04","slug":"testing-immutable-entities-with-nhibernate","status":"publish","type":"post","link":"https:\/\/www.jameskovacs.com\/index.php\/2007\/11\/04\/testing-immutable-entities-with-nhibernate\/","title":{"rendered":"Testing Immutable Entities with NHibernate"},"content":{"rendered":"<p>In a domain model, you will sometimes encounter immutable data &#8211;&nbsp;objects that can only be read from a database, but never modified. The question is how to test your persistence layer to ensure that you can read them. One way is to hard code values into your tests and assume that the appropriate rows are in the database. This is rather fragile as you are counting on your database being in a known good state. This also prevents you (or another developer) from getting latest from source control onto a clean developer workstation and being able to run the unit tests immediately. You must populate&nbsp;a test&nbsp;database with known good data first. Here&#8217;s a little trick you can play with NHibernate. Let&#8217;s say you have the following class:<\/p>\n<pre class=\"code\"><span style=\"color: rgb(0,0,255)\">namespace<\/span> JamesKovacs.Examples.ImmutableData {\n    <span style=\"color: rgb(0,0,255)\">public<\/span> <span style=\"color: rgb(0,0,255)\">class<\/span> <span style=\"color: rgb(43,145,175)\">Invoice<\/span> {\n        <span style=\"color: rgb(0,0,255)\">private<\/span> <span style=\"color: rgb(0,0,255)\">readonly<\/span> <span style=\"color: rgb(0,0,255)\">int<\/span> m_Id = 0;\n        <span style=\"color: rgb(0,0,255)\">private<\/span> <span style=\"color: rgb(0,0,255)\">readonly<\/span> <span style=\"color: rgb(0,0,255)\">string<\/span> m_InvoiceNumber;\n        <span style=\"color: rgb(0,0,255)\">private<\/span> <span style=\"color: rgb(0,0,255)\">readonly<\/span> <span style=\"color: rgb(0,0,255)\">decimal<\/span> m_Amount;\n        <span style=\"color: rgb(0,0,255)\">private<\/span> <span style=\"color: rgb(0,0,255)\">readonly<\/span> <span style=\"color: rgb(43,145,175)\">DateTime<\/span> m_InvoiceDate;\n\n        <span style=\"color: rgb(0,0,255)\">public<\/span> Invoice() {\n        }\n\n        <span style=\"color: rgb(0,0,255)\">public<\/span> Invoice(<span style=\"color: rgb(0,0,255)\">string<\/span> invoiceNumber, <span style=\"color: rgb(0,0,255)\">decimal<\/span> amount, <span style=\"color: rgb(43,145,175)\">DateTime<\/span> invoiceDate) {\n            m_Amount = amount;\n            m_InvoiceDate = invoiceDate;\n            m_InvoiceNumber = invoiceNumber;\n        }\n\n        <span style=\"color: rgb(0,0,255)\">public<\/span> <span style=\"color: rgb(0,0,255)\">int<\/span> Id {\n            <span style=\"color: rgb(0,0,255)\">get<\/span> { <span style=\"color: rgb(0,0,255)\">return<\/span> m_Id; }\n        }\n\n        <span style=\"color: rgb(0,0,255)\">public<\/span> <span style=\"color: rgb(0,0,255)\">string<\/span> InvoiceNumber {\n            <span style=\"color: rgb(0,0,255)\">get<\/span> { <span style=\"color: rgb(0,0,255)\">return<\/span> m_InvoiceNumber; }\n        }\n\n        <span style=\"color: rgb(0,0,255)\">public<\/span> <span style=\"color: rgb(43,145,175)\">DateTime<\/span> InvoiceDate {\n            <span style=\"color: rgb(0,0,255)\">get<\/span> { <span style=\"color: rgb(0,0,255)\">return<\/span> m_InvoiceDate; }\n        }\n\n        <span style=\"color: rgb(0,0,255)\">public<\/span> <span style=\"color: rgb(0,0,255)\">decimal<\/span> Amount {\n            <span style=\"color: rgb(0,0,255)\">get<\/span> { <span style=\"color: rgb(0,0,255)\">return<\/span> m_Amount; }\n        }\n    }\n}<\/pre>\n<p><a href=\"http:\/\/11011.net\/software\/vspaste\"><\/a><\/p>\n<p>And its corresponding NHibernate mapping file:<\/p>\n<pre class=\"code\"><span style=\"color: rgb(0,0,255)\">&lt;?<\/span><span style=\"color: rgb(163,21,21)\">xml<\/span><span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">version<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">1.0<\/span>\"<span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">encoding<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">utf-8<\/span>\"<span style=\"color: rgb(0,0,255)\"> ?&gt;\n&lt;<\/span><span style=\"color: rgb(163,21,21)\">hibernate-mapping<\/span><span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">xmlns<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">urn:nhibernate-mapping-2.2<\/span>\"\n<span style=\"color: rgb(0,0,255)\">                   <\/span><span style=\"color: rgb(255,0,0)\">namespace<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">JamesKovacs.Examples.ImmutableData<\/span>\"\n<span style=\"color: rgb(0,0,255)\">                   <\/span><span style=\"color: rgb(255,0,0)\">assembly<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">JamesKovacs.Examples.ImmutableData<\/span>\"\n<span style=\"color: rgb(0,0,255)\">                   <\/span><span style=\"color: rgb(255,0,0)\">default-access<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">field.pascalcase-m-underscore<\/span>\"<span style=\"color: rgb(0,0,255)\">&gt;\n  &lt;<\/span><span style=\"color: rgb(163,21,21)\">class<\/span><span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">name<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"I<span style=\"color: rgb(0,0,255)\">nvoice<\/span>\"<span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">mutable<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">false<\/span>\"<span style=\"color: rgb(0,0,255)\">&gt;\n    &lt;<\/span><span style=\"color: rgb(163,21,21)\">id<\/span><span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">name<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">Id<\/span>\"<span style=\"color: rgb(0,0,255)\">&gt;\n      &lt;<\/span><span style=\"color: rgb(163,21,21)\">generator<\/span><span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">class<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">native<\/span>\"<span style=\"color: rgb(0,0,255)\"> \/&gt;\n    &lt;\/<\/span><span style=\"color: rgb(163,21,21)\">id<\/span><span style=\"color: rgb(0,0,255)\">&gt;\n<\/span><span style=\"color: rgb(0,0,255)\">    &lt;<\/span><span style=\"color: rgb(163,21,21)\">property<\/span><span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">name<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">InvoiceNumber<\/span>\"<span style=\"color: rgb(0,0,255)\">\/&gt;\n    &lt;<\/span><span style=\"color: rgb(163,21,21)\">property<\/span><span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">name<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">Amount<\/span>\"<span style=\"color: rgb(0,0,255)\">\/&gt;\n    &lt;<\/span><span style=\"color: rgb(163,21,21)\">property<\/span><span style=\"color: rgb(0,0,255)\"> <\/span><span style=\"color: rgb(255,0,0)\">name<\/span><span style=\"color: rgb(0,0,255)\">=<\/span>\"<span style=\"color: rgb(0,0,255)\">InvoiceDate<\/span>\"<span style=\"color: rgb(0,0,255)\">\/&gt;\n<\/span><span style=\"color: rgb(0,0,255)\">  &lt;\/<\/span><span style=\"color: rgb(163,21,21)\">class<\/span><span style=\"color: rgb(0,0,255)\">&gt;\n&lt;\/<\/span><span style=\"color: rgb(163,21,21)\">hibernate-mapping<\/span><span style=\"color: rgb(0,0,255)\">&gt;<\/span><\/pre>\n<p>Note that the Invoice is lookup data only. So it is declared as immutable in the mapping file (mutable=&#8221;false&#8221;). NHibernate will not permit INSERT, UPDATE, or DELETE operations against the entity. The question is how do we test this? We could circumvent NHibernate and directly insert test data into the table via ADO.NET. The downside is that you&#8217;re coupling your test data generation code to a particular database schema. If the database schema changes, you must not only update the NHibernate mapping, but you must also modify your SQL statements for creating test data. A definite violation of DRY (Don&#8217;t Repeat Yourself) Principle. Let&#8217;s see if there is a better way&#8230;<\/p>\n<p>NHibernate can generate the database schema from mapping files using NHibernate.Tool.hbm2ddl.SchemaExport.&nbsp;The problem is that you can&#8217;t insert data into the Invoice table since the type is mapped as immutable. However, you can&nbsp;read the NHibernate configuration and mapping files and modify the mapping at runtime. This is done by cfg.GetClassMapping(typeof(Invoice)).IsMutable = true. We&#8217;re switching our immutable entity to a mutable one on the fly for the purposes of populating test data! Note that you have to modify the mapping before creating your SessionFactory.<\/p>\n<pre class=\"code\"><span style=\"color: rgb(0,0,255)\">using<\/span> System;\n<span style=\"color: rgb(0,0,255)\">using<\/span> JamesKovacs.Examples.ImmutableData;\n<span style=\"color: rgb(0,0,255)\">using<\/span> MbUnit.Framework;\n<span style=\"color: rgb(0,0,255)\">using<\/span> NHibernate;\n<span style=\"color: rgb(0,0,255)\">using<\/span> NHibernate.Cfg;\n<span style=\"color: rgb(0,0,255)\">using<\/span> NHibernate.Tool.hbm2ddl;\n\n<span style=\"color: rgb(0,0,255)\">namespace<\/span> JamesKovacs.Examples.ImmutableData.Tests {\n    [<span style=\"color: rgb(43,145,175)\">TestFixture<\/span>]\n    <span style=\"color: rgb(0,0,255)\">public<\/span> <span style=\"color: rgb(0,0,255)\">class<\/span> <span style=\"color: rgb(43,145,175)\">InvoiceRepositoryTests<\/span> {\n        <span style=\"color: rgb(0,0,255)\">private<\/span> <span style=\"color: rgb(0,0,255)\">const<\/span> <span style=\"color: rgb(0,0,255)\">string<\/span> InvoiceNumber = <span style=\"color: rgb(163,21,21)\">\"ABCD1234\"<\/span>;\n        <span style=\"color: rgb(0,128,0)\">\/\/ N.B. You should do this once before all your repository tests<\/span>\n        <span style=\"color: rgb(0,128,0)\">\/\/      as creating a SessionFactory and executing DDL is time-consuming.<\/span>\n       [<span style=\"color: rgb(43,145,175)\">SetUp<\/span>]\n        <span style=\"color: rgb(0,0,255)\">public<\/span> <span style=\"color: rgb(0,0,255)\">void<\/span> Setup() {\n            <span style=\"color: rgb(43,145,175)\">Configuration<\/span> cfg = <span style=\"color: rgb(0,0,255)\">new<\/span> <span style=\"color: rgb(43,145,175)\">Configuration<\/span>().Configure();\n            <span style=\"color: rgb(43,145,175)\">SchemaExport<\/span> exporter = <span style=\"color: rgb(0,0,255)\">new<\/span> <span style=\"color: rgb(43,145,175)\">SchemaExport<\/span>(cfg);\n            exporter.Execute(<span style=\"color: rgb(0,0,255)\">false<\/span>, <span style=\"color: rgb(0,0,255)\">true<\/span>, <span style=\"color: rgb(0,0,255)\">false<\/span>, <span style=\"color: rgb(0,0,255)\">false<\/span>);\n            cfg.GetClassMapping(<span style=\"color: rgb(0,0,255)\">typeof<\/span>(<span style=\"color: rgb(43,145,175)\">Invoice<\/span>)).IsMutable = <span style=\"color: rgb(0,0,255)\">true<\/span>;\n            <span style=\"color: rgb(43,145,175)\">ISessionFactory<\/span> sessionFactory = cfg.BuildSessionFactory();\n            <span style=\"color: rgb(0,0,255)\">using<\/span>(<span style=\"color: rgb(43,145,175)\">ISession<\/span> session = sessionFactory.OpenSession()) {\n                session.BeginTransaction();\n                <span style=\"color: rgb(43,145,175)\">Invoice<\/span> invoice = <span style=\"color: rgb(0,0,255)\">new<\/span> <span style=\"color: rgb(43,145,175)\">Invoice<\/span>(InvoiceNumber, 42m, <span style=\"color: rgb(43,145,175)\">DateTime<\/span>.Today);\n                session.Save(invoice);\n                session.Transaction.Commit();\n            }\n        }\n\n        [<span style=\"color: rgb(43,145,175)\">Test<\/span>]\n        <span style=\"color: rgb(0,0,255)\">public<\/span> <span style=\"color: rgb(0,0,255)\">void<\/span> CanRetrieveInvoice() {\n            <span style=\"color: rgb(0,0,255)\">using<\/span>(<span style=\"color: rgb(43,145,175)\">UnitOfWork<\/span>.Start()) {\n                <span style=\"color: rgb(43,145,175)\">IInvoiceRepository<\/span> repo = <span style=\"color: rgb(0,0,255)\">new<\/span> <span style=\"color: rgb(43,145,175)\">InvoiceRepository<\/span>();\n                <span style=\"color: rgb(43,145,175)\">Invoice<\/span> invoice = repo.FindByInvoiceNumber(InvoiceNumber);\n                <span style=\"color: rgb(43,145,175)\">Assert<\/span>.IsNotNull(invoice);\n                <span style=\"color: rgb(43,145,175)\">Assert<\/span>.AreEqual(InvoiceNumber, invoice.InvoiceNumber);\n            }\n        }\n    }\n}<\/pre>\n<p>So we now have an elegant solution for populating immutable test data in a clean database without resorting to hand-coded SQL.<\/p>\n<p>N.B. UnitOfWork.Start() sets up an NHibernate session that is used internally by the repositories. It is based on Oren Eini&#8217;s UnitOfWork implementation in Rhino Commons.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a domain model, you will sometimes encounter immutable data &#8211;&nbsp;objects that can only be read from a database, but never modified. The question is how to test your persistence layer to ensure that you can read them. One way is to hard code values into your tests and assume that the appropriate rows are [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-115","post","type-post","status-publish","format-standard","hentry","category-dotnetgeneral"],"_links":{"self":[{"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/posts\/115","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/comments?post=115"}],"version-history":[{"count":0,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/posts\/115\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/media?parent=115"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/categories?post=115"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jameskovacs.com\/index.php\/wp-json\/wp\/v2\/tags?post=115"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}