This is the first part of a multi-part series. In this series we will cover the scenario of creating a multiple choice exam for the user. In the first part I will cover the design of the application. This will include unit tests, domain objects and NHibernate mapping files.

Introduction:

 

This is the first part of a multi-part series. In this series we will cover the scenario of creating a multiple choice exam for the user. In the first part I will cover the design of the application. This will include unit tests, domain objects and NHibernate mapping files.

 

The Requirements:

 

A school named “Geek School” requires a web application that can be used to give exams to their students. They require a page to upload the exam. The exam will be provided in an XML format. Each exam will consist of several questions. Each question will have multiple choices.

 

The Domain Objects:

 

The requirement indicates that there are three entities involved in this application, Exam, Question and Choice. Let’s take a look at the class diagram for the domain layer.

 

 

All the entity classes inherit from the DomainBase class. The DomainBase class consists of the fields and the properties which are used by all the classes. Let’s take a look at the implementation of the DomainBase class.

 

public class DomainBase

    {

        private int _id;

        private bool _isValid;

 

        public int Id

        {

            get { return _id; }          

        }

 

        protected virtual bool IsValid

        {

            get { return _isValid; }

            set { _isValid = value; }

        }

    }

 

 

I am going to show you some of the interesting properties and methods of the Exam, Question and Choice class. If you are interested in the complete code then please use the download link at the bottom of the article.

 

Let’s start with the Exam class. Exam can have Title, TotalPoint and Questions. Title is just a simple String type so we are not concerned about it right now. Questions are a list of Question entities and are defined as IList<Question> in the Exam object. 

 

private IList<Question>  _questions = new List<Question>();

public IList<Question> Questions

        {

            get { return new ReadOnlyCollection<Question>(_questions); }          

        }

 

In the code above that I am returning the List<Question> as a ReadOnlyCollection. This is because I don’t want the user to modify the collection. If the user wishes to add a question to the Exam then he should always use the AddQuestion method.

 

public void AddQuestion(Question question)

        {

            if (_questions.Contains(question)) return;

 

            _questions.Add(question);

 

            question.Exam = this;

        }

 

The TotalPoint property of the Exam returns the total points for the exam. The point is related to the Question since each question has certain point. The Exam object gets the total points by iterating through the Question collection and adding the points. This is a better technique then using NHibernate Derived Properties as I have discussed it here.

 

The question class has a reference to the Exam object. This indicates that the question belongs to a certain exam.

 

The domain objects shown above were created after writing Unit Tests. You should always start the application by writing Unit Tests. This will make the design better and the process easier.

 

Creating the Mapping Files:

 

The mapping files are used by NHibernate to map the relationships between different domain objects and to perform persistence operations on the objects. I have placed all the mapping files in the Mappings directory in the DomainObjects project. Let’s take a look at the Exam mapping file.

 

<hibernate-mapping  assembly="DomainObjects"

  namespace="DomainObjects"

     xmlns:xsd="http://www.w3.org/2001/XMLSchema"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xmlns="urn:nhibernate-mapping-2.2"

  default-lazy="false">

 

  <class name="Exam, DomainObjects" table="Exams" lazy="false">

    <id name="Id" column="ExamID" type="Int32" access="nosetter.camelcase-underscore">

      <generator class="identity" />

    </id>

 

    <property name="Title" column="Title" type="String" not-null="true"/>

   

    <bag name="Questions" access="nosetter.camelcase-underscore" lazy="false" cascade="save-update" inverse="true">

 

      <key column="ExamID" />

      <one-to-many class="Question" />

     

    </bag>  

   

  </class>

 

</hibernate-mapping>

 

Let’s break it down so that we can understand it better.

 

<class name="Exam, DomainObjects" table="Exams" lazy="false">

 

The class name element describes the name of the class included in the domain. In this case we have the Exam class and the DomainObjects assembly. The table attribute refers to the table in the database which is “Exams”.

 

<id name="Id" column="ExamID" type="Int32" access="nosetter.camelcase-underscore">

      <generator class="identity" />

    </id>

 

The id element refers to the unique Id of the objects which is mapped to the column in the database. Think of it as the primary key of the table. The access modifier indicates the id cannot be set externally.

 

<property name="Title" column="Title" type="String" not-null="true"/>

 

The property element defines the property in the class. In the above example I am defining the “Title” property which is of type String and cannot be null.

 

    <bag name="Questions" access="nosetter.camelcase-underscore" lazy="false" cascade="save-update" inverse="true">

 

      <key column="ExamID" />

      <one-to-many class="Question" />     

    </bag>  

 

The bag indicates the IList<T> collection. The above configuration indicates that the Exam consists of a bag of Questions defined by the Questions property. The lazy = “false” indicates that when the exam is loaded all the questions of the exam are also loaded. The cascade attribute indicates that what operations are performing on the child objects when the root object is changed. The inverse property indicates that the relationship is two sided meaning that we can use Exam to get questions and question to get the exam.

 

The element “key” defines the foreign key which is used in the Question class. And finally the one-to-many element defines which class is involved in the relationship which in this case is the “Question” class.

 

You can look at the Question and Choice mapping file which are available in the download.

 

The Domain Logic Unit Tests:

 

The Unit Tests were written before writing the Domain Objects. Here are few of the tests in the ExamFixture class.

 

[TestMethod]

        public void CanAddQuestionsToExam()

        {

            Exam exam = new Exam();

            exam.AddQuestion(new Question());

            exam.AddQuestion(new Question());

 

            Assert.AreEqual(exam.Questions.Count, 2);

        }

 

        [TestMethod]

        public void CanCalculateTotalPointsForAnExam()

        {

            Exam exam = new Exam();

            exam.AddQuestion(new Question("Q1",10));

            exam.AddQuestion(new Question("Q2",10));

 

            Assert.AreEqual(exam.TotalPoint, 20.0);

        }

 

You can view the other unit tests by downloading the sample.

 

Single Responsibility Principle in Unit Tests:

 

Single Responsibility Principle states that every object should have only one responsibility. This principle is also applied on Methods, Functions and Classes. The other day I was Unit Testing and started thinking that why not apply the same principle in unit tests.

 

Take a look at the following unit test.

 

[TestMethod]

 

public void CanAddExam()

 

{

 

Exam exam = new Exam();

 

exam.Title = "New Exam";

 

ExamManager.Save(exam);

 

Assert.IsTrue(exam.Id != 0);

 

Exam eVerify = ExamManager.GetById(exam.Id);

 

Assert.AreEqual(exam.Title, eVerify.Title);

 

}

 

 

 

In the above code I am testing that if the object exam is inserted in the database. Now, take a look at the following code where I test the deletion of object.

[TestMethod]

 

public void CanRemoveExam()

 

{

 

Exam exam = new Exam();

 

exam.Title = "New Exam";

 

ExamManager.Save(exam);

 

Assert.IsTrue(exam.Id != 0);

 

Exam eVerify = ExamManager.GetById(exam.Id);

 

Assert.AreEqual(exam.Title, eVerify.Title);

 

ExamManager.Delete(eVerify);

 

Assert.IsNull(ExamManager.GetById(eVerify));

 

}

 

 

 

The test works fine and give the correct result. But the problem is that I am repeating the previous test in my new test. Take a look at the following lines in the CanRemoveExam test.

 

Assert.IsTrue(exam.Id != 0);

 

Assert.AreEqual(exam.Title, eVerify.Title);

 

 

These two lines were also in the CanAddExam tests. The test CanRemoveExam is now verifying two separate conditions. It verifies that the object got inserted and then it verifies the object got deleted. It is breaking the Single Responsibility Principle. The test CanRemoveExam should look like this:

 

public void CanRemoveExam()

 

{

 

Exam exam = new Exam();

 

exam.Title = "New Exam";

 

ExamManager.Save(exam);

 

Exam eVerify = ExamManager.GetById(exam.Id);

 

ExamManager.Delete(eVerify);

 

Assert.IsNull(ExamManager.GetById(eVerify));

 

}

 

 

 

Now, it has only one responsibility. One may argue that when running the CanRemoveExam test how will be find out that the object actually got inserted. Well, that is the job of a different test which is "CanAddExam". If CanAddExam test passes then we are sure that there are no problems when inserting objects into the database.

 

This will make the unit tests smaller and easier to debug since now we will know that which Assert condition actually got broken.

 

Conclustion:

 

In this article we discussed the design aspects of the application. In the next article we will see how to upload and display the exam to the user.

 

I hope you liked the article, happy programming!