12 August, 2014

Unit testing with authentication

When we are developing an application that will be integrating with third party system, we often face the problem of testing some methods, that are using common session, which ID is being returned to us as a result of Authentication process. I have met this problem many times, when I was making integration with ERP systems, WMS's and lately, with eCommerce. I was wondering: how the hell I can write the tests of many methods, using the same session, and make them independent?

I have found some ideas on the blogs and in the articles:
  • Some developers were advising to create one huge test with many assertions, which will start with Login (Authenticate, LogOn, StartSession etc.) method and end with Logout (CloseSession, LogOff, etc). This idea is OK, but it is NOT a UNIT test.
  • Other developers were advising to use some unit testing libraries that enable passing the state between test methods. This idea is also OK, but this is the way of creating integration tests, not the unit tests!
  • The last idea was to log in before and log out after every operation in each unit test. This will also work, but you have to repeat a lot of code, which is a very bad habit.

Finally I have found a solution: IUseFixture interface in xUnit. The framework is very easy to use and unit testing of e.g. API of third party web services is very easy. Here is an example...

Imagine, you want to run some methods of a WS*, which is an API for the ERP system. You want to take some data from the WS*, but you need to be logged in and have a session id. The best way to do this is to use IUseFixture interface.
It is a generic interface, which bases on type, that must by disposable. For example if you want to create session before running each test method and close it after running of each method, you can create your base type like this:

using ThirdPartySystemTests.Service;
using System;

namespace ThirdPartySystemTests
{
    public class SessionFixture : IDisposable
    {
        private SessionHelper sessionHelper;

        public SessionHelper LogOnUser()
        {
          sessionHelper = new SessionHelper();
          sessionHelper.Client = new ThirdPartySystemClient();
          sessionHelper.SessionId = sessionHelper.Client.login
                                   (
                                    sessionHelper.LOGIN, 
                                    sessionHelper.PASSWORD
                                    );
            return sessionHelper;
        }

        public void Dispose()
        {
            bool sessionClosed = 
              sessionHelper.Client.endSession(sessionHelper.SessionId);
        }
    }

    public class SessionHelper
    {
        public ThirdPartySystemClient Client { get; set; }
        public string SessionId { get; set; }
        public string LOGIN = "User";
        public string PASSWORD = "Password";
    }
}


This disposable type has 2 methods: first method creates session on the ThirdPartySystem and the second one closes this session on disposal of the SessionFixture object.
Now it is time to implement tests of ThirdPartySystem using xUnit test with our SessionFixture.

using ThirdPartySystemTests.Service;
using System;
using Xunit;

namespace ThirdPartySystemTests
{
    public class Tests : IUseFixture
    {
        private SessionHelper sessionHelper;

        public void SetFixture(SessionFixture sessionFixture)
        {
            sessionHelper = sessionFixture.LogOnUser();
        }

        [Fact]
        private void GetProductsListTest()
        {
            Product[] products = 
               sessionHelper.Client.GetProductList(sessionHelper.SessionId);
            Assert.NotEmpty(products);
        }

        [Fact]
        private void GetCustomersListTest()
        {
            Customer[] customers = 
               sessionHelper.Client.GetCustomersList(sessionHelper.SessionId);
            Assert.NotEmpty(customers);
        }

        [Fact]
        private void CreateProductTest()
        {
            Prodcut product = new Product
            {
                Name = "Laptop",
                Price = 1000,
                Quantity = 20
            };
            int productId = 
                 sessionHelper.Client.CreateProduct(sessionHelper.SessionId, product);
            Assert.True(productId > 0);
        }
    }
}

Now the LogOnUser method will run before each execution of every test in the class, that implements IUseFixture so you have a new session every time the test runs. What is more, on disposal of the IUseFixture (it means at the end of execution of the test) the session will be closed.

As a result, you have a independent set of unit tests, which do not have any connections and can be run separately. This solution is elegant, easy, safe and it observes the principles of unit tests.

You can also use IUseFixture to e.g. run methods from the base types, create configuration of some objects in the database, prepare data for further operations etc.

0 comments:

Post a Comment