Click here to Skip to main content
14,359,091 members

WCF Service Unit Tests Using NUnit With Rhino Mocks And Entity Framework

Rate this:
4.83 (3 votes)
Please Sign up or sign in to vote.
4.83 (3 votes)
20 Nov 2016CPOL
In this post we will see how we can write unit test cases for our WCF Service with a framework called NUnit. We will also be covering how to mock our dependencies in our test, here we wil be using Rhino Mocks. I am going to use Visual Studio 2015 for the development. I hope […]

In this post we will see how we can write unit test cases for our WCF Service with a framework called NUnit. We will also be covering how to mock our dependencies in our test, here we wil be using Rhino Mocks. I am going to use Visual Studio 2015 for the development. I hope you will like this article.

Download source code

Background

As a developer, we all writes lots of codes in our day to day life. Am I right? It is more important to check whether the codes we have written works well. So for that we developer usually do unit testing, few developers are doing a manual testing to just check whether the functionality is working or not. I would say that it is wrong. In a TDD (Test Driven Development) unit testing is very important, where we actually writes the test cases before we start our coding. Let us see what exactly the “Unit Tesing” is.

Unit Testing

Unit testing is the process of testing a unit, it can be a class, a block of codde, function, a property. We can easily test our units independently. In dot net we have so many frameworks to do unit testing. But here we are going to use NUnit which I found very easy to write tests.

If you have Resharper installed in your machine, it will be more easier to execute and debug your tests. Here I am using Resharper in my Visual Studio, so the screenshots will be based on that. Thank you

Now it is time to set up our project and start our coding.

Setting up the project

To get started, please create an empty project in your Visual Studio.

empty_project

empty_project

Now, we will add a WCF Service as follows.

create_a_wcf_service

create_a_wcf_service

Once you are done, you can see two files, an Interface(IMyService) and a class (MyService) with .svc extension. If you are completely new to WCF service, I strongly recommend you to read some basics here.

Now, it is time to set up our database and insert some data.

Creating database

Here I am creating a database with name “TrialDB”, you can always create a DB by running the query given below.

USE [master]
GO

/****** Object:  Database [TrialDB]    Script Date: 20-11-2016 03:54:53 PM ******/
CREATE DATABASE [TrialDB]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'TrialDB', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS\MSSQL\DATA\TrialDB.mdf' , SIZE = 8192KB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'TrialDB_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS\MSSQL\DATA\TrialDB_log.ldf' , SIZE = 8192KB , MAXSIZE = 2048GB , FILEGROWTH = 65536KB )
GO

ALTER DATABASE [TrialDB] SET COMPATIBILITY_LEVEL = 130
GO

IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [TrialDB].[dbo].[sp_fulltext_database] @action = 'enable'
end
GO

ALTER DATABASE [TrialDB] SET ANSI_NULL_DEFAULT OFF 
GO

ALTER DATABASE [TrialDB] SET ANSI_NULLS OFF 
GO

ALTER DATABASE [TrialDB] SET ANSI_PADDING OFF 
GO

ALTER DATABASE [TrialDB] SET ANSI_WARNINGS OFF 
GO

ALTER DATABASE [TrialDB] SET ARITHABORT OFF 
GO

ALTER DATABASE [TrialDB] SET AUTO_CLOSE OFF 
GO

ALTER DATABASE [TrialDB] SET AUTO_SHRINK OFF 
GO

ALTER DATABASE [TrialDB] SET AUTO_UPDATE_STATISTICS ON 
GO

ALTER DATABASE [TrialDB] SET CURSOR_CLOSE_ON_COMMIT OFF 
GO

ALTER DATABASE [TrialDB] SET CURSOR_DEFAULT  GLOBAL 
GO

ALTER DATABASE [TrialDB] SET CONCAT_NULL_YIELDS_NULL OFF 
GO

ALTER DATABASE [TrialDB] SET NUMERIC_ROUNDABORT OFF 
GO

ALTER DATABASE [TrialDB] SET QUOTED_IDENTIFIER OFF 
GO

ALTER DATABASE [TrialDB] SET RECURSIVE_TRIGGERS OFF 
GO

ALTER DATABASE [TrialDB] SET  DISABLE_BROKER 
GO

ALTER DATABASE [TrialDB] SET AUTO_UPDATE_STATISTICS_ASYNC OFF 
GO

ALTER DATABASE [TrialDB] SET DATE_CORRELATION_OPTIMIZATION OFF 
GO

ALTER DATABASE [TrialDB] SET TRUSTWORTHY OFF 
GO

ALTER DATABASE [TrialDB] SET ALLOW_SNAPSHOT_ISOLATION OFF 
GO

ALTER DATABASE [TrialDB] SET PARAMETERIZATION SIMPLE 
GO

ALTER DATABASE [TrialDB] SET READ_COMMITTED_SNAPSHOT OFF 
GO

ALTER DATABASE [TrialDB] SET HONOR_BROKER_PRIORITY OFF 
GO

ALTER DATABASE [TrialDB] SET RECOVERY SIMPLE 
GO

ALTER DATABASE [TrialDB] SET  MULTI_USER 
GO

ALTER DATABASE [TrialDB] SET PAGE_VERIFY CHECKSUM  
GO

ALTER DATABASE [TrialDB] SET DB_CHAINING OFF 
GO

ALTER DATABASE [TrialDB] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF ) 
GO

ALTER DATABASE [TrialDB] SET TARGET_RECOVERY_TIME = 60 SECONDS 
GO

ALTER DATABASE [TrialDB] SET DELAYED_DURABILITY = DISABLED 
GO

ALTER DATABASE [TrialDB] SET QUERY_STORE = OFF
GO

USE [TrialDB]
GO

ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 0;
GO

ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET MAXDOP = PRIMARY;
GO

ALTER DATABASE SCOPED CONFIGURATION SET LEGACY_CARDINALITY_ESTIMATION = OFF;
GO

ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET LEGACY_CARDINALITY_ESTIMATION = PRIMARY;
GO

ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = ON;
GO

ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET PARAMETER_SNIFFING = PRIMARY;
GO

ALTER DATABASE SCOPED CONFIGURATION SET QUERY_OPTIMIZER_HOTFIXES = OFF;
GO

ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET QUERY_OPTIMIZER_HOTFIXES = PRIMARY;
GO

ALTER DATABASE [TrialDB] SET  READ_WRITE 
GO

Create a table and insert data in database

To create a table, you can run the query below.

USE [TrialDB]
GO

/****** Object:  Table [dbo].[Course]    Script Date: 20-11-2016 03:57:30 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Course](
	[CourseID] [int] NOT NULL,
	[CourseName] [nvarchar](50) NOT NULL,
	[CourseDescription] [nvarchar](100) NULL,
 CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED 
(
	[CourseID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Now we can insert few data to our newly created table.

USE [TrialDB]
GO

INSERT INTO [dbo].[Course]
           ([CourseID]
           ,[CourseName]
           ,[CourseDescription])
     VALUES
           (1
           ,'C#'
           ,'Learn C# in 7 days')
 INSERT INTO [dbo].[Course]
           ([CourseID]
           ,[CourseName]
           ,[CourseDescription])
     VALUES
           (2
           ,'Asp.Net'
           ,'Learn Asp.Net in 7 days')
INSERT INTO [dbo].[Course]
           ([CourseID]
           ,[CourseName]
           ,[CourseDescription])
     VALUES
           (3
           ,'SQL'
           ,'Learn SQL in 7 days')
INSERT INTO [dbo].[Course]
           ([CourseID]
           ,[CourseName]
           ,[CourseDescription])
     VALUES
           (4
           ,'JavaScript'
           ,'Learn JavaScript in 7 days')
GO

So our data is ready, that means we are all set to write our service and tests. Now go to your solution and create an entity data model.

entity_framework

entity_framework

So entity is also been created. Now please open your interface and that is where we start our coding. We can change the interface as follows.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WCF_NUnit_Tests_Rheno_Mocks
{
    [ServiceContract]
    public interface IMyService
    {
        [OperationContract]
        Course GetCourseById(int courseId);
        [OperationContract]
        List<Course> GetAllCourses();
    }
}

Here we have created two operations, one to get a course by id and one to retrieve all the courses as a list. Now please go and implement these two operations in our service file. You can modify that class as follows.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WCF_NUnit_Tests_Rheno_Mocks
{
    public class MyService : IMyService
    {
        private static MyEntity _myContext;
        private static IMyService _myIService;

        public MyService()
        {
            
        }

        public MyService(IMyService myIService)
        {
            _myContext = new MyEntity();
            _myIService = myIService;
        }
        public Course GetCourseById(int courseId)
        {
            var crse = _myContext.Courses.FirstOrDefault(dt => dt.CourseID == courseId);
            return crse;
        }

        public List<Course> GetAllCourses()
        {
            var courses = (from dt in _myContext.Courses select dt).ToList();
            return courses;
        }
    }
}

In the above code, as you can see we are creating two constructor one is without parameter and other is with parameter, and we are having IMyService as a parameter. In this way we can achieve the dependency injection when we write tests for our unit. So what we need to do all is, just pass the dependency, in this case it is IMyService.

In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. Source: WikiPedia

If you need to know more on dependency injection, please read here. Now we will build and check whether our service is working fine or not. Please press CTRL+F5.

invoking_wcf_service

invoking_wcf_service

As our services are ready, we can now create the tests for those operations. For that we can create a new class library in our project and name it UnitTest.Service. Please add a class MyServiceTests in the class library where we can add our tests. And please do not forget to add our application reference too.

add_project_reference

add_project_reference

Installing and configuring NUnit

Now we can install NUnit to our test project from NuGet Package. Once you add the package, you will be able to add the preceding namespace in our MyServiceTests class.

using NUnit.Framework;

In NUnit we have so many attributes that can be used for different purposes, but now we are going to use only four among them.

  • TestFixture
testfixture_in_nunit

testfixture_in_nunit

  • OneTimeSetUp
one_time_setup_attribute_in_nunit

one_time_setup_attribute_in_nunit

In previous versions, we were using TestFixtureSetUp, as the TestFixtureSetUp is obsolete, now we are using OneTimeSetUp

testfixturesetup_attribute_is_obsolete

testfixturesetup_attribute_is_obsolete

  • TearDown

This attribute is used to identify a method that is called immediately after each tests, it will be called even if there is any error, this is the place we can dispose our objects.

  • Test

This attribute is used to make a method callable from NUnit test runner. This can not be inherited.

Now we can see all these attributes in action. So let us write some tests, but the real problem is we need to mock the IMyService right as the parameterized constructor of the class MyService expecting it. Remember, we have discussed about setting up our services in the way which can be injected the dependencies? No worries, we can install Rhino Mock for that now.

rhino_mocks_in_nuget_package

rhino_mocks_in_nuget_package

So we can add the tests are dependencies as follows in our test class.

using NUnit.Framework;
using Rhino.Mocks;
using WCF_NUnit_Tests_Rhino_Mocks;

namespace UnitTest.Service
{
    [TestFixture]
    public class MyServiceTests
    {
        private static MyService _myService;
        private IMyService _myIservice;
        [OneTimeSetUp]
        public void SetUp()
        {
            _myIservice = MockRepository.GenerateMock<IMyService>();
            _myService = new MyService(_myIservice);
            
        }

        [TearDown]
        public void Clean()
        {
           
        }

        [Test(Description = "A test to check whether the returned value is null")]
        public void GetCourseById_Return_NotNull_Pass()
        {
            //Set Up
            var crs = new Course
            {
                CourseID = 1,
                CourseName = "C#",
                CourseDescription = "Learn course in 7 days"
            };
            _myIservice.Stub(dt => dt.GetCourseById(1)).IgnoreArguments().Return(crs);

            //Act
            crs = _myService.GetCourseById(1);

            //Assert
            Assert.IsNotNull(crs,"The returned value is null");
        }

        [Test(Description = "A test to check we get all the courses")]
        public void GetAllCourses_Return_List_Count_Pass()
        {
            //Act
            var crs = _myService.GetAllCourses();

            //Assert
            Assert.AreEqual(4, crs.Count,"The count of retrieved data doesn't match");
            _myIservice.VerifyAllExpectations();
        }

    }
}

As you can see we have mocked our IMyService as follows.

_myIservice = MockRepository.GenerateMock<IMyService>();
generate_mock_with_rhino

generate_mock_with_rhino

And, in the test GetCourseById_Return_NotNull_Pass we have also used a method called Stub. Stub actually tell the mock object to perform a certain action when a matching method is called, and it doesn’t create an expectation for the same. So you might be thinking, how can we create an expectation? For that we have a method called Expect.

expect_in_rhino_mock

expect_in_rhino_mock

It is always recommended to verify your expectation when you use Expect as we used it in our test GetAllCourses_Return_List_Count_Pass.

_myIservice.VerifyAllExpectations();

As I already said, I am using Resharper, we have so many shortcuts to run our tests, now if you right click on your TestFixture. You can see a run all option as preceding.

run_all_test_option_in_resharper

run_all_test_option_in_resharper

As I was getting an error as “No connection string named ‘Entity’ could be found in the application config file.” when I run the tests, I was forced to install the entity framework in my test project and also add a new config file with the connection string like we have in our web config file.

If everything goes fine and you don’t have any errors, I am sure you will get a screen as preceding.

nunit_output

nunit_output

Happy coding!.

See also

Conclusion

Did I miss anything that you may think which is needed? Could you find this post as useful? I hope you liked this article. Please share me your valuable suggestions and feedback.

Your turn. What do you think?

A blog isn’t a blog without comments, but do try to stay on topic. If you have a question unrelated to this post, you’re better off posting it on C# Corner, Code Project, Stack Overflow, Asp.Net Forum instead of commenting here. Tweet or email me a link to your question there and I’ll definitely try to help if I can.

Kindest Regards
Sibeesh Venu

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sibeesh Passion
Software Developer
Germany Germany
I am Sibeesh Venu, an engineer by profession and writer by passion. I’m neither an expert nor a guru. I have been awarded Microsoft MVP 3 times, C# Corner MVP 5 times, DZone MVB. I always love to learn new technologies, and I strongly believe that the one who stops learning is old.

My Blog: Sibeesh Passion
My Website: Sibeesh Venu

Comments and Discussions

 
-- There are no messages in this forum --
Article
Posted 20 Nov 2016

Stats

6.5K views
3 bookmarked