In my previous posts, we had a look at testability for serverless applications. In this post, we’re going to apply a similar approach to Azure WebJobs.
The sample code used in this post can be found here.
For either scheduled WebJob instance or on-demand WebJob instance, it’s just a console application that we’re not dealing with. However, when we look at the continuous running WebJob instance, it will be a different story. Let’s have a look at the basic WebJob code.
This is the main WebJob host entry. Within the Main()
method, it creates the JobHostConfiguration
instance and JobHost
instance for a WebJob application that is continuously running. This is the actual WebJob function.
As we can see, it has the static
modifier, which make us hard to test. We can use the service locator pattern for this static
method to cope with testability like Azure Function triggers. However, this should only apply when the service locator is the only option. Luckily, Azure WebJobs provides an interface called IJobActivator
, which helps us easily integrate WebJob with an IoC container. WebJobActivator.Autofac is one of implementations of the IJobActivator
with Autofac. Let’s have a look how the NuGet package has implemented the interface.
DISCLAIMER: I am the author of the NuGet package, WebJobActivator.Autofac.
Extending IJobActivator
When we decompile the IJobActivator
interface, we see that it contains only one method CreateInstance()
, which is insufficient for any IoC container library because we need to register dependencies beforehand. Therefore, we need to extend the interface, called IWebJobActivator
. The new interface declares another method, RegisterDependencies(THandler handler = default(THandler))
.
As the RegistrationHandler
class is optional, so we can omit it for actual implementation. But the handler is rather useful for unit testing. We’ll touch this later in this post. Here’s the abstract class that implements IWebJobActivator
:
As we can see, the implemented method barely does registration. We need an IoC container library here.
Integrating with Autofac
Now, let’s implement the extended interface with Autofac. Here’s the actual implementation:
With Autofac, the AutofacJobActivator
does actual dependencies registration process. One of the most powerful features that Autofac offers is Module
. We can register dependencies by grouping as a module. For our purpose, the RegistrationHandler
can be a perfect place to implement this module. Here’s the handler implementations:
As we can see, AutofacRegistrationHandler
implements a property of Action
to register a module. Here’s the sample module:
We’re all set. Let’s change the Program.cs
to apply IJobActivator
.
Enabling IJobActivator
The Main()
method can be modified like this:
Inside the method, we instantiate AutufacRegistrationHandler
and register WebJobModule
, instantiate AutofacJobActivator
and register dependencies using the handler, and add the activator to JobHostConfiguration
. Now, we can finally remove all the static
modifier from the function class:
So far, we’ve extended IJobActivator
to IWebJobActivator
and implemented AutofacJobActivator
to configure JobHost
. However, I’m still not happy because there are two instances remain tightly coupled within the Main()
method – JobHostConfiguration
and JobHost
. We also need to decouple those two instances.
Implementing JobHostConfigurationBuilder
JobHostConfigurationBuilder
is basically a wrapper class of JobHostconfiguration
, with a fluent method, AddConfiguration(Action)
. Let’s have a look.
This builder class gets the activator instance as a parameter and internally creates an instance of the JobHostConfiguration
. Now, we’ve got JobHostConfiguration
decoupled. Let’s move on.
Implementing JobHostBuilder
This is the last step. JobHostBuilder
is another wrapper class of JobHost
and here’s the implementation.
Nothing special. It gets the JobHostConfigurationBuilder
, internally creates a JobHostConfiguration
instance, create a JobHost
instance, and run the host instance. This now needs to be integrated with Autofac like:
AutofacJobHostBuilder
accepts Autofac’s module instance which help register dependencies. We’ve all set now. Let’s make another change on the Program.cs
:
Putting Them Altogether
This is the final version of Program.cs
with full testability.
As a static property, WebJobHost
gets the AutofacJobHostBuilder
class with dependencies and additional configurations. After that, within the Main()
method, it builds JobHost and runs the instance. While performing unit tests, this Program.cs
can be also testable by mocking and injecting a dependency of IJobHostBuilder
like:
So far, we’ve walked through how an Azure WebJob application can be written by being decoupled and considering testability. With this approach, we don’t even need to run Storage Emulator in our local environment for testing. Happy coding!