REQUEST A DEMO

Workday Integration via web services: Dealing with deep object graphs

We’re wrapping up a Workday integration via their web services. The objects we’re dealing with in the web services calls to Workday contain a lot of information in very deep object graphs that are not necessarily initialized. I learned a couple of new tricks this time around with web services that makes the consumption and related testing of these objects relatively painless. To be clear, you’ll likely run into this scenario working with any web services in .NET. I just happened to be working with Workday web services when I decided to write this post.

The first thing I need to do is get the Workday web services response object mapped to a type for all of my code to consume. I don’t have any interest at all in writing a ton of left / right code that looks like this:

public MappedEmployee MapWorkdayObject(WorkerType workerType)
{
    var returnValue = new MappedEmployee();
    returnValue.FirstName = string.Empty;
    if(workerType.Worker_Data.Personal_Data != null &&
        workerType.Worker_Data.Personal_Data.Name_Data != null &&
        workerType.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data != null &&
        workerType.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data != null) 
    {
        returnValue.FirstName = workerType.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.First_Name;
    }

    ...
    ...
    ...
    return returnValue; 
}

Ugh…just to map a simple string property from Workday’s web services I’ve got four null checks. Furthermore, in order to test this simple left / right code I’ve got to test the happy path and then I have to add four tests to make sure I’ve properly written all of my null checking code. Fortunately for me, we use AutoMapper here at Dovetail. Mapping one object to another is exactly the problem AutoMapper solves. It also supports null substitution so I don’t have to write all the code to account for nulls myself and then write tests for it. Here’s how we map first name with AutoMapper:

map.ForMember(x => x.FirstName,
    opt =>
    {
        opt.NullSubstitute(string.Empty);
        opt.MapFrom(m => m.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.First_Name);                                            
    });

That’s a lot better. The opt.NullSubstitute line takes care of all the null checking I had to do above and then I’m left with the one line I’m really interested in. The next thing I’ve got to do is test the mapping. :

[Test]
public void TestFirstNameMap()
{
    var source = new WorkerType();
    source.Worker_Data = new Worker_DataType();
    source.Worker_Data.Personal_Data = new Personal_Information_DataType();
    source.Worker_Data.Personal_Data.Name_Data = new Person_Name_DataType();
    source.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data = new Legal_Name_DataType();
    source.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data = new Person_Name_Detail_DataType();
    source.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.First_Name = "firstName";

    var dest = new WorkdayWorkerData();
    Assert.AreEqual("firstName", Mapper.Map(source, dest).FirstName);           
}

That’s a lot of code just to test the mapping for first name is properly configured. The reason I’ve got to write all the initialization code for my source object is the same reason I had to write all the null checking code. I’ve got an object with a deep object graph that’s not initialized. What I’d really like here is for my test to have minimal code involved with arranging the input. I want my test to focus on what I’m testing, not on verbose setup code. I’d like my test to look something like this:

[Test]
public void TestFirstNameMap()
{        
    source.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.First_Name = "firstName";
    var dest = new WorkdayWorkerData();
    Assert.AreEqual("firstName", Mapper.Map(source, dest).FirstName);           
}

Now that I know what I want my test to look like, the only thing left to figure out is how do I get an initialized object that I can just set values on. I’ve got a lot of options to do that. Extension methods, helper classes, base test class, partial classes with constructors that initialize everything. The real problem with all of those approaches is that I’ve got to write all that code. I’d prefer a solution where I didn’t have to write any of it. Enter AutoFixture. From the AutoFixture home page on GitHub:

AutoFixture is an open source framework for .NET designed to minimize the ‘Arrange’ phase of your unit tests. Its primary goal is to allow developers to focus on what is being tested rather than how to setup the test scenario, by making it easier to create object graphs containing test data.

Wow, that’s exactly what I need! I installed AutoFixture via nuget and within minutes I had this test class:

[TestFixture]
public class AutoMapperTest
{
    private WorkerType _source;
    private WorkdayWorkerData _dest;

    [SetUp]
    public void SetUp()
    {
        Mapper.Reset();
        var fixture = new Fixture { RepeatCount = 1 };
        _source = fixture.Create();
        _dest = fixture.Create();

        //The automapper code is in here for clarity.  The initialization code should not be in your tests.
        var mappingExpression = Mapper.CreateMap<workertype, workdayworkerdata="">();
        mappingExpression.ForMember(x => x.FirstName,
            opt =>
            {
                opt.NullSubstitute(string.Empty);
                opt.MapFrom(x => x.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.First_Name);
            });
    }

    [Test]
    public void TestFirstNameMap()
    {            
        _source.Worker_Data.Personal_Data.Name_Data.Legal_Name_Data.Name_Detail_Data.First_Name = "firstName";
        Assert.AreEqual("firstName", Mapper.Map(_source, _dest).FirstName);            
    }
}

Much better! I’ve got a small amount of setup code (that I can move out of my test fixture if I wish to do so). My test is concise and concerned only with arranging the system into the state I want to test. If you’re working with objects with deep object graphs, hopefully you learned some tricks to make working with them a bit easier.

%d bloggers like this: