Introduction
We are all familiar with a very common scenario while coding in a business application. In fact, this is the most frequent scenario where we want to save an object to the database. But how would we know that our code is functioning properly? Well, there are many ways we could know. Some are good ways to follow and some are not. Let’s discuss using a scenario.
Example Scenario
Suppose we want to save a Student
object where the class will have Name
, Phone
, Email
and Id
. To save this object, we should create a repository class named StudentRepository
. In this class, we will write our data saving codes.
Now there are some points I want to mention. It is good practice to create an interface for each of the repository classes. It is a subset of Repository Pattern.
In our case, we should create IStudentRepository
where we will define only Save
method and make the StudentRepository
class to implement this interface.
So, in summary, we have three projects. Those are DataAccess
, DataAccess.Contract
, and Domain
up to this state. Below are the class definitions and project architecture.
Entity Framework
For implementing the database access code, we will not use traditional SqlClient
library classes and don't write the traditional SQL queries. Rather, we will use the Entity Framework instead. Entity Framework (EF) is an object-relational mapper that enables .NET developers to work with relational data using domain-specific objects. It eliminates the need for most of the data-access code that developers usually need to write.
To use that framework in our project, we need to install that. But where would we find that library? Well. Microsoft has developed an extension by which we can install, update and uninstall the external libraries to our project in a very easy way. Below is how can we install the Entity Framework into our DataAccess
Project..
Walkthrough: Install Entity Framework using Nuget
- Right click on the reference option of the
DataAccess
project. - Find the Entity Framework item (second in the below picture).
- Click the “install” button of that item.
- Click Accept when you are asked for the license agreement.
- After successful installation, you should see the green sign instead of the “install” button.
After installing the entity framework, we should create a class DataContext
which will inherit DBContext
class and we will map our class Student
with the table name under OnModelCreating
method. When the DataContext
class is called for the first time, the Entity Framework will look for the Database and create the database by itself if that does not exist. Next, it will create the tables according to the instruction we gave in OnModelCreating
method. From the below class, the system will create a database named MockingPractices.DataAccess.DataContext
in .\SQLEXPRESS
server (since we didn't give any connection string, it will do it in the default server). And also, the Student
table is created.
The Database
The DataContext Class
namespace MockingPractices.DataAccess
{
using System.Data.Entity;
using MockingPractices.Domain;
public class DataContext:DbContext
{
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().ToTable("Student");
base.OnModelCreating(modelBuilder);
}
}
}
The IStudentRepository Interface
namespace MockingPractices.DataAccess.Contract
{
using MockingPractices.Domain;
public interface IStudentRepository
{
int Save(Student student);
}
}
The StudentRepository Class
namespace MockingPractices.DataAccess
{
using MockingPractices.DataAccess.Contract;
using MockingPractices.Domain;
public class StudentRepository : IStudentRepository
{
public int Save(Student student)
{
using (var context=new DataContext())
{
Student studentAdded = context.Students.Add(student);
context.SaveChanges();
return studentAdded.Id;
}
}
}
}
Unit Testing
Now we will find a way to test our codes. In a tradition way, we would have to create a Simple User Interface (might be Windows Forms Application) where there will be a button. If we click the button, we will create a student
object and pass it to the repository method. Below is the scenario.
Class Diagram
User Interface Based Testing Architecture
But if we frequently change our code and need to test, then every time we must run the UI and hit the button for test. Things got worse if we need to test multiple scenarios for a single repository save
method. So, what we can understand is that we need a system which can automatically manage the calling of those methods. We don’t need to click and click the user interface for each change of the development code. Below may be one of the proposed architectures.
Automated Testing Architecture
This type of testing is named as automated unit testing and currently, there are a couple of frameworks for this purpose. NUnit and Visual Studio’s internal Unit testing framework both are very common. Let’s use the second one in our solution. Below is how you can create a Unit Test project to your solution. If you still don't get the idea, don't worry. Just keep reading below. " />
Go to: Add New Project-->Visual C# --> Test --> Unit Test Project
Give an appropriate name and then press OK button.
Each of the unit testing frameworks has some classes by which the compiler understands to which classes it should run. This tag is TestClass
for this and the required test methods must have TestMethod
tag on top of it.
In our case, we can rename the default class to StudentRepositoryTest
class and rename the given template method to SaveShouldReturnSavedId
method.
So, the test method now looks like below:
Philosophy of the Unit Testing
Let’s begin with a very sad but true scenario. Suppose one day you are in a romantic mode, want to talk with your lover and you say “I Love You Jaan”. What do you expect to hear? We all expect “I love you too honey” or similar types of line in reply. But if your lover shouts at you then that shouting will definitely not be matched with your expectation. Am I right?
In the programming world, we expect our method to work smoothly without any problem. We know exactly what to expect from a specific method. In our scenario, we expect our repository method to save the student
object and returns us the saved id of that object. And we also know that Id must not be zero because our database’s primary key is an auto incremented number and there is no way for the object to be saved in the id 0 row. So, our expectation is the returned value should be not equal to zero. Right? Makes sense?
So how would we implement that scenario in our code?
There is a class named Assert
made by Microsoft. Its duty is to match the expected value to the returned/actual value. If the expectation matches with the actual, it shows green but if the expectation is mismatched, it shows red. So the code will look like below:
namespace MockingPractices.DataAccess.Test
{
using System.Transactions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MockingPractices.DataAccess.Contract;
using MockingPractices.Domain;
[TestClass]
public class StudentRepositoryTest
{
[TestMethod]
public void SaveShouldReturnSavedId()
{
using (TransactionScope scope = new TransactionScope())
{
IStudentRepository studentRepository = new StudentRepository();
Student student = new Student()
{
Id = 0,
Name = "test name",
Email = "testemail@email.com",
Phone = "123456"
};
int id = studentRepository.Save(student);
Assert.AreNotEqual(0, id);
}
}
}
}
Now if we follow the below images, we can instruct the system to run the test method and execute the code inside that method.
Walkthrough: Run the Test Method Using Visual Studio Unit Testing Framework
- Move your cursor to the icon given in the below image.
- Click on that and press Run.
- A window will appear which indicates the status of the running test.
- If the test runs successfully, the following image should appear.
If you strictly follow my above instructions, you should now see the green sign as given below. Thus, we can test our database code using unit testing without the help of any kinds of User Interface.
But there are some problems in the above work.
- We should not retain our test data into the database and
- We should not create the helping objects by us. Below is the way in which we can solve those problems.
The main rule is that we must revert the database insert
operation after the test
method. We can use TransactionScope
class for that.
Secondly, we should use a framework to create those helping stubs. We will use NBuilder
to build the objects for us. Installation of NBuilder
can be done using Nuget.
You should see the installed packages in your project by going to the below image:
So, after coding to solve these problems, our code should look like this:
namespace MockingPractices.DataAccess.Test
{
using System.Transactions;
using FizzWare.NBuilder;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MockingPractices.DataAccess.Contract;
using MockingPractices.Domain;
[TestClass]
public class StudentRepositoryTest
{
[TestMethod]
public void SaveShouldReturnSavedId()
{
using (TransactionScope scope = new TransactionScope())
{
IStudentRepository studentRepository = new StudentRepository();
Student student = Builder<Student>.CreateNew().With(s => s.Id = 0).Build();
int id = studentRepository.Save(student);
Assert.AreNotEqual(0, id);
}
}
}
}
Run as many as you like the test just pressing the Run command. You will never need to open the user interface again and again and input the values and calculate the expected values by yourself. This is the true beauty of unit testing.