Getting started with NDbUnit

Contents
  1. Introduction
  2. Steps to writing a test that uses NDbUnit
  3. Supported Database Operations
Introduction

One property of a well-written unit test is that of repeatability. Repeatability means that two executions of the same test will result in the same outcome, assuming that the environment external to the test remains the same. Running a test should result in the same outcome, regardless of what the test does, whether the test passes or fails, and even whether the test didn't run to completion due to an exception or error.

NDbUnit helps increase repeatability in unit tests that interact with a database by ensuring that the database's state is consistent across execution of tests. Before or after the execution of each test, NDbUnit may perform an operation that initializes or finalizes the database.

Steps to writing a test that uses NDbUnit
  1. Create your dataset schema.
    • The schema describes the tables and their columns that may appear in your dataset. Normally, one would use Visual Studio to create the schema, although this isn't strictly required.
    • The dataset schema should include all primary key definitons, as well as foreign key relationships between tables.
    • Here is an example schema that includes one table:
  2. <?xml version="1.0" encoding="utf-8" ?> <xs:schema id="Facility" targetNamespace="http://tempuri.org/Facility.xsd" elementFormDefault="qualified" attributeFormDefault="qualified" xmlns="http://tempuri.org/Facility.xsd" xmlns:mstns="http://tempuri.org/Facility.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="Facility" msdata:IsDataSet="true"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="FACILITY"> <xs:complexType> <xs:sequence> <xs:element name="FACILITYPK" type="xs:string" /> <xs:element name="NAME" type="xs:string" /> <xs:element name="FACILITYID" type="xs:string" /> <xs:element name="DESCRIPTION" type="xs:string" /> <xs:element name="ACTIVESTATUS" type="xs:string" /> <xs:element name="FACILITYTYPEID" type="xs:string" /> <xs:element name="CUSTOMERID" type="xs:string" /> <xs:element name="DEANUMBER" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:unique name="FacilityKey1" msdata:PrimaryKey="true"> <xs:selector xpath=".//mstns:FACILITY" /> <xs:field xpath="mstns:FACILITYPK" /> </xs:unique> </xs:element> </xs:schema>
  3. Create your dataset.
    • Your dataset should be created using the standard Microsoft dataset xml format. Simple datasets may be created by hand. More complex datasets may be created in code by building a dataset object from select statements, and calling dataset.ToXML().
    • Note that the name of the root xml element, FacilityTestDS, is the name of the dataset, and will have no impact on the data in the database.
    • Here is an example dataset that contains two rows:
  4. <?xml version="1.0" encoding="utf-8" ?> <FacilityTestDS xmlns="http://tempuri.org/Facility.xsd"> <FACILITY> <FACILITYPK<FACILITY-0000000000-000000000-01</FACILITYPK> <NAME>Green Hospital</NAME> <FACILITYID>GreenHospital</FACILITYID> <DESCRIPTION>Green Hospital</DESCRIPTION> <ACTIVESTATUS>FacilityStatusActive</ACTIVESTATUS> <FACILITYTYPEID>FacilityTypeMyFacility</FACILITYTYPEID> <CUSTOMERID>Customer ID1</CUSTOMERID> <DEANUMBER>DEANumber 1</DEANUMBER> </FACILITY> <FACILITY> <FACILITYPK>FACILITY-0000000000-000000000-02</FACILITYPK> <NAME>Sharp Hospital</NAME> <FACILITYID>SharpHospital</FACILITYID> <DESCRIPTION>Sharp Hospital</DESCRIPTION> <ACTIVESTATUS>FacilityStatusActive</ACTIVESTATUS> <FACILITYTYPEID>FacilityTypeOtherFacility</FACILITYTYPEID> <CUSTOMERID>Customer ID2</CUSTOMERID> <DEANUMBER>DEANumber 2</DEANUMBER> </FACILITY> </FacilityTestDS>
  5. Add a SqlDbUnitTest member variable, and code to initialize it in your SetUp method.
    • The SqlDbUnitTest class supports both a file-based and a stream-based API.
    • Here, mySchema and myDataSet may be either strings representing the name of the file to load, or Stream objects.
    • Here is an example SetUp method:
  6. private SqlDbUnitTest test; [SetUp] public void SetUp() { this.test = new SqlDbUnitTest( connectionString ); this.test.ReadXmlSchema( mySchema ); this.test.ReadXml( myDataSet ); // // additional setup... // }
  7. Add code to perform the appropriate DB operation in your SetUp and TearDown methods.
    • The operation you choose will depend on what your test is doing. For example, a test for deleting something might perform a CleanInsert operation, whereas a test for inserting something might perform a Delete operation.
    • Here is an example of performing a Refresh operation in the SetUp method.
  8. private SqlDbUnitTest test; [SetUp] public void SetUp() { this.test = new SqlDbUnitTest( connectionString ); this.test.ReadXmlSchema( mySchema ); this.test.ReadXml( myDataSet ); this.test.PerformDbOperation( DbOperationFlag.Refresh ); // // additional setup... // }
  9. Implement your test methods.
    • Your tests will now be running with a database that has been initialized in a consistent state.
Supported Database Operations
  1. DbOperationFlag.CleanInsert
  2. <?xml version='1.0' encoding='UTF-8'?> <WordDataSet> <WORD> <WORD_ID>5</WORD_ID> <SPELLING>ossify</SPELLING> <PART_OF_SPEECH>Verb</PART_OF_SPEECH> </WORD> </WordDataSet>
    Snap shot before operation:
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- 1 Adjective pugnacious 2 Adjective glib 3 Verb wamble (3 rows affected)
    After operation- notice how only our new word exists.
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- 5 Verb ossify (1 row affected)

  3. DbOperationFlag.DeleteAll
  4. <?xml version='1.0' encoding='UTF-8'?> <WordDataSet> <WORD> <WORD_ID>1</WORD_ID> <SPELLING>pugnacious</SPELLING> <PART_OF_SPEECH>Adjective</PART_OF_SPEECH> </WORD> </WordDataSet>
    Snap shot before operation:
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- 1 Adjective pugnacious 2 Adjective glib (2 rows affected)
    After operation- everything has been removed.
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- (0 rows affected)

  5. DbOperationFlag.Delete
  6. <?xml version='1.0' encoding='UTF-8'?> <WordDataSet> <WORD> <WORD_ID>1</WORD_ID> <SPELLING>pugnacious</SPELLING> <PART_OF_SPEECH>Adjective</PART_OF_SPEECH> </WORD> </WordDataSet>
    Snap shot before operation:
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- 1 Adjective pugnacious 2 Adjective glib (2 rows affected)
    After operation- notice how only word_id #1 has been removed.
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- 2 Adjective glib (1 row affected)

  7. DbOperationFlag.Insert
    <?xml version='1.0' encoding='UTF-8'?> <WordDataSet> <WORD> <WORD_ID>5</WORD_ID> <SPELLING>ossify</SPELLING> <PART_OF_SPEECH>Verb</PART_OF_SPEECH> </WORD> </WordDataSet>
    Snap shot before operation:
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- 1 Adjective pugnacious 2 Adjective glib 3 Verb wamble (3 rows affected)
    After operation- notice how a new word has been added.
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- 1 Adjective pugnacious 2 Adjective glib 3 Verb wamble 5 Verb ossify (4 rows affected)

  8. DbOperationFlag.Refresh
    <?xml version='1.0' encoding='UTF-8'?> <WordDataSet> <WORD> <WORD_ID>1</WORD_ID> <SPELLING>cantankerous</SPELLING> <PART_OF_SPEECH>Adjective</PART_OF_SPEECH> </WORD> <WORD> <WORD_ID>6</WORD_ID> <SPELLING>puckish</SPELLING> <PART_OF_SPEECH>Adjective</PART_OF_SPEECH> </WORD> </WordDataSet>
    Snap shot before operation:
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ---------- 1 Adjective pugnacious 2 Adjective glib (2 rows affected)
    After operation- notice how "puckish" has been added and word_id #1 has been updated to "cantankerous" as we saw in DbOperationFlag.Refresh
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ------------ 1 Adjective cantankerous 2 Adjective glib 6 Adjective puckish (3 rows affected)

  9. DbOperationFlag.Update
  10. <?xml version='1.0' encoding='UTF-8'?> <WordDataSet> <WORD> <WORD_ID>1</WORD_ID> <SPELLING>cantankerous</SPELLING> <PART_OF_SPEECH>Adjective</PART_OF_SPEECH> </WORD> <WORD> <WORD_ID>6</WORD_ID> <SPELLING>puckish</SPELLING> <PART_OF_SPEECH>Adjective</PART_OF_SPEECH> </WORD> </WordDataSet>
    Snap shot before operation:
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ------------ 1 Adjective pugnacious 2 Adjective glib (2 rows affected)
    After operation- notice how no new word has been added and word_id #1 has been updated to "cantankerous"
    1> select * from word; 2> go WORD_ID PART_OF_SPEECH SPELLING ----------- -------------- ------------ 1 Adjective cantankerous 2 Adjective glib (2 rows affected)