Introduction
This is the third article to guide those who would like to contribute to DNN Platform on how to write tests. In this article, I will expand on the previous articles (Part 1 and Part 2) and show you how to start adding new test features. I will base the tests on a real code that was contributed by some community members.
Why Integration Tests
Due to the way some parts of the DNN code were written over the years it might not be possible to write unit tests as it needs a huge amount of stubbing and mocking, even, in some cases, it might not be possible at all. Therefore, we need to write integration tests which operate on an actual installed DNN site and a SQL server database.
DNN has created a small framework to facilitate in this task by providing utilities and helper classes to makes it easy to create users and login as these users and call WEB API methods, etc. These library helpers are packaged as a NuGet package and published to the public feed of DNN packages here
Besides, integration tests usually have no mocks or stubs to write which makes writing them a bit simpler in this area but have to deal with different concerns of its own such as sessions and others which makes the tests larger when it comes to coding. They only need to setup the proper values of the tested site; such as, the URL to the site, the connection sting to the database, any app-settings related to the site. These are usually added to the “App.config” file of the test project.
Adding Your First Integration Test(s)
In the following discussion I will show you how to add integration tests to the existing Platform code base. I will walk you through creating a few tests for the “MemberDirectoryController.cs” WEB API methods by sending requests, validating the responses, and checking the results in the database. Unlike the tests in part 2, there is no Jira and Github pull request associated with these practice tests.
To perform WEB API requests, you either need to have the source code to find out what are the parameters names and type that are passed with the request or use some tools to inspect the wire messages that go on the network. The browser tools (which are available by pressing F12 key) are there for your help. Or you can use standalone tools; such as, Fiddler, but these are outside the scope of this article. For this tutorial, I will assume you are testing a code that you have written or already have access to so that you know the internals of the tests.
Let’s have a look at the member directory WEB API service in the file “MemberDirectoryController.cs”. In this file there are many API methods utilizing the GET and POST verbs. Let’s pick a few that can be the base for this demo. I’ll assume the following scenario:
-
Create user 1 and user 2 using provided helpers in the framework
-
User 1 logs in and sends a friend request to user 2.
-
We check the database contains the friend request.
-
User 2 logs in and accepts the friendship request.
-
Again we check the database to validate this.
For this purpose we will be using the following methods:
[HttpPost]
public HttpResponseMessage AddFriend(FriendDTO postData)
[HttpPost]
public HttpResponseMessage AcceptFriend(FriendDTO postData)
So, we start by:
- navigating to “DotNetNuke.Tests.Integration”
- selects the “Tests” folder under it
- add a new folder called “MemberDirectory”
- add a class under it called “MemberDirectoryTests.cs”
- modify the new class to inherit from the “IntegrationTestBase” class (this is the integration framework class that simplifies many things for writing the tests)
- create an empty positive test method and add the NUnit necessary attributes to the code
The final result should look something like this:
using DNN.Integration.Test.Framework;
using NUnit.Framework;
namespace
DotNetNuke.Tests.Integration.Tests.MemberDirectory
{
[TestFixture]
public class MemberDirectoryTests : IntegrationTestBase
{
[Test]
public void Send_And_Accept_Friendship_Requests_Positive_Test()
{
Assert.Fail();
}
}
}
Identifying the WEP API endpoint
To know what the endpoints for the WEB API methods is, we need to look into the “MemberDirectoryServiceRouteMapper.cs” which contains the following code:
mapRouteManager.MapHttpRoute(
"MemberDirectory",
"default",
"{controller}/{action}",
new[] { "DotNetNuke.Modules.MemberDirectory.Services" });
And the file “MemberDirectoryController.cs” contains:
public class MemberDirectoryController : DnnApiController
…
[HttpPost]
public HttpResponseMessage AddFriend(FriendDTO postData)
From the above we can formulate the endpoint to call the add friend method will be:
POST http://<site-url>/api/MemberDirectory/MemberDirectory/AddFriend
In the above, the first “MemberDirectory” is the MapHttpRoute’s first argument (in the first code snippet) while the second one is the controller name without the “Controller” text part (in the second code snippet), and the last part of the endpoint “AddFriend” is the method we are calling. All these parts are highlighted in the code above.
Adding Integration Test Code
Note: before proceeding you need to make sure you updated the test project’s “App.config” file values to match the site you are testing against.
We start our coding by creating the two users we need to use for the tests. To achieve this, we utilize the available helpers in the integration testing framework. I advise you to get yourself familiar with these helpers as they will reduce the code amount, the time, and the effort to add tests.
The test contains many actions and assertion that need some explanation which I added as comments in the code itself. Here is the full file:
using System;
using DNN.Integration.Test.Framework;
using DNN.Integration.Test.Framework.Helpers;
using NUnit.Framework;
namespace
DotNetNuke.Tests.Integration.Tests.MemberDirectory
{
[TestFixture]
public class MemberDirectoryTests : IntegrationTestBase
{
private const string AddFriendQuery = "/api/MemberDirectory/MemberDirectory/AddFriend";
private const string AcceptFriendQuery = "/api/InternalServices/RelationshipService/AcceptFriend";
[Test]
public void Send_And_Accept_Friendship_Requests_Positive_Test()
{
int userId1,
fileId1, userId2, fileId2;
string username1, username2;
// create two test users - will create a user
with name "testuser" padded with 4
// random numeric digits and obtain a connection
(session) for the new user.
// the connector/session will have the user
logged in when successful.
var
connector1 = WebApiTestHelper.PrepareNewUser(out userId1, out username1, out fileId1);
var
connector2 = WebApiTestHelper.PrepareNewUser(out userId2, out username2, out fileId2);
Console.WriteLine(@"Test users
=> {0} and {1}", connector1.UserName, connector2.UserName);
// make sure there are no pending notifications
so we can check for the add-friend notification
DatabaseHelper.ExecuteNonQuery("DELETE FROM {objectQualifier}CoreMessaging_Messages");
var
notificationsCount = DatabaseHelper.ExecuteScalar<int>(
"SELECT COUNT(*) FROM
{objectQualifier}CoreMessaging_Messages");
Assert.AreEqual(0, notificationsCount);
// Send Add friend request from user 1 to user 2.
We must add the proper headers which
// we obtain from calling GetRequestHeaders for
the members directory page.
// Note that
without the proper request headers, the call might not be successful.
var response1
= connector1.PostJson(AddFriendQuery, new { friendId = userId2 },
WebApiTestHelper.GetRequestHeaders("Member Directory"));
// Make sure the request succeeded
Assert.IsTrue(response1.IsSuccessStatusCode);
// Get friend request from database and check
there is one
var
notificationId = DatabaseHelper.ExecuteScalar<int>(
"SELECT TOP 1 MessageID FROM
{objectQualifier}CoreMessaging_Messages WHERE SenderUserID="
+
userId1);
Assert.Greater(notificationId, 0);
// let user 2 approve the request
connector2.PostJson(AcceptFriendQuery, new { NotificationId = notificationId });
// make sure the notfication wa sprocessed and
removed
notificationsCount = DatabaseHelper.ExecuteScalar<int>(
"SELECT COUNT(*) FROM
{objectQualifier}CoreMessaging_Messages");
Assert.AreEqual(0, notificationsCount);
}
}
}
Notice how there is a mix of API calls to the actual web site and at the same time we check the database to assert some values were added to, or removed from, the database. Also, notice the use of the object qualifies of the database “{objectQualifier}”. If you let these out, your tests will pass on the sites which do not use qualifiers, but if the site is using it, then the tests will surely fail.
I will not add negative tests in this tutorial, and will leave it as an exercise for you to see what happens if you send a request to non-existing user.
Submitting Your Tests
As in the previous article, you have written these tests then what next? It is time now for committing your changes and pushing them to the repository then creating a pull request.
Summary
I just touched the basics of adding integration tests in DNN. The existing tests in the Platform repository have many more examples for you to learn from. So, don’t hesitate to have a look and send me any questions you like as comments to this series of blogs.