Microsoft Teams Bot - How to achieve the impossible? - Part 4

April 17, 2018 / Bots, AI, Cognition October 22, 2018


Dependency Injection

In the early stages of development everything is simple, straight forward and clean. Well, at least most of the times. But let’s imagine that kind of situation – let’s say, that we have some kind of „DataService“ that is used to access some remote data. The easiest way to access that is to create instance of that service whenever it’s needed. At start it may not be the worst idea ever. But let’s assume, that during development we have the need to use that multiple times. We can always create some property in the class with the initialization of that service. If that case occurs more than in one class – just create some base class that implement that, or even we can go even further – create some factory for that services and call that factory to do so.

Yeah, that’s for sure better solution than initializing instance in every call (believe me, in some projects that happens!). So let’s imagine other case – we have our perfectly well working factory, and we want to create some unit tests. But as we want to create tests as „atomic“ as possible, we would not like to have additional dependencies. And inside of that class we call that factory. So what now? Should we create another factory? Maybe we should every single time when class is created pass that factory as parameter? And what in case, when we need other class that uses instance of any other class/service that depends on? Should we pass all of them to every single other instances?

Nah, that’s too complicated!

And here comes dependency injection.

Overall concept

There are many IOC (Inversion of Control) containers available on the market this days, but the main principle of all of them is more less the same. To make this as simple as possible – there is an „container“, that contains information about class implementations. To make it even better – information about interfaces and classes that implements that. And classes that needs to use any of the element from the container just „informs“ that container about the need. How? For example, by adding that „declaration“ as constructor parameter (constructor injection):

So now, whenever LUISDialog (or – ILUISDialog interface object) is „requested“ in the same way, as ISubscriptionService or IProactivebotService, both of that parameters will be populated by those elements.

Technical aspects

As was mentioned before, there are many IOC containers, and in this particular project the one that was chosen is Autofac. Why that in particular? It is used by Bot Framework, so at least libraries are there. Autofac has many extensions to make it easily work in many possible scenarios, so additional few libraries were added – for MVC and Web.API:

In the application start required registration has been made.

As you can see, that’s not the place where type registrations are made, but that’s inside that Register method marked red, and that’s the place where more interesting things happens. 
Firs example – registration without interface, in that case – for controllers. They are not used anywhere but during the calls, so there was no need to create interfaces for them. Registrations is straight forward:

But you may ask – „Ok, if that’s only used during the calls, so that object won’t be directly requested, so why to even bother with registration?“. That would be very good point. Actually – that’s not the whole code, as it looks like:

Those parameters are simply strings. So in the constructor of that controller, I can simply request string values that will be provided by the IOC Container:

And as you can see, those strings are not the only one – I’m also requesting some objects that are implementing particular interfaces. I don’t need to register them as the parameters, what I need to do, is to register types and interfaces:

And in this case, the same as in the previous example, string parameter is provided as well.

Pros and cons?

Let’s start with pros. First of all – you have complete control of the process of creating instances of the classes. You can adjust lifespan, you can create singleton instances etc. What’s more – change of implementation can be completely seamless, as only change that is required is in the registration class. And because of that – even if new implementation requires for example other set of parameters in constructor, it can be provided as well. And from the perspective of classes/methods that uses that you have nothing to worry about. In case of that project it also helped to tidy up configurations from .config file, what was essential in further tasks.

Are there any cons? The only I can see is one-time configuration. It may look quite straight forward and easy but can cause problems as well. For example – I had some problems with serialization of the service instances inside of the dialogs (I even didn’t want them to serialize!). In that case registration of SurrogateModule helped:

Either way – pros greatly outweighs cons, and after configuration it can help in keeping project in order, and later on – even make development easier.

Introduction to the tests

I believe I don’t need to say what the unit tests are. And how big is its importance. In case of „dynamic development“ of the project it’s important to keep track of main functionalities. Verification every time can be time consuming, so automated tests are the best way to go. Especially, when we can arrange some dialog, and thanks to the access to the source code – we can predict the expected outcome.

Unit Tests

In most of the tests there is one common element – models. Whether it’s model for the bot itself, or Outlook, Teamwork, whatever, always there are some fields that needed to be filled to create more less meaningful test case.

What is not a surprise, it’s quite time consuming. And we would like not to waste our time for filling models with data. Luckily, there is „nuget package“ for that as well 😉

NBuilder

The library of my choice is NBuilder. It provides nice fluent interface for building objects and collections of the objects. So for example, let’s assume, that we want to create single instance of the simple object.

Done!

What if we need list of that objects?

Done.

What about nested objects and lists?

And that’s it. And now when we have those test objects, we can continue writing tests.

Unfortunately, we cannot test everything. Or maybe we could, but it would not be as convenient as we would like it to be. For example – how can we test, whether time entry is created? Or if the selected project exists?

For that purpose, we can create Mock’s, that will handle those requests, and based on them – will return always the same response, in the same format, as the real server would.

Thanks to the constructor injection of models introduced in previous paragraph, we can create our own implementation of the data provider, what will implement our interface. Then – we can easily use that for test purposes. For example – in this case, result of the call is determined on expected output:

And having that implemented, we can create simple test that will verify, if method that obtains data does correct data modifications and returns expected output:

And in the similar way other we can test other services as well. That’s not perfect, but at least we can be sure, that because of changes in our code for example of some condition we didn’t break main functionality, and projects are still returned.

That’s only simple example, but you get the idea.

Much more complicated is tests of the bot itself.

Bot framework Tests

As there is no simple helper for that, nor there is available any great helper in form of nuget package (at least at the moment of writing that blog entry), we need to take another approach. We can always take advantage of the fact, that Bot Framework is open source, and tests are created by the Microsoft team itself. Whole source code of the Bot Builder can be found at LINK.

For our need we need to get three particular classes available in the tests, and they are:

  • ConversationTests.cs
  • DialogTestBase.cs
  • FiberTests.cs

From this classes we need to remove unit tests of their own (missing some references), and then we can make use of them.

In this classes fake bot context is build, so we can from code perspective make effective communication with the bot. Now, in the test class we need to initialize the context:

And implement some method, that will help us in sending messages:

And thanks to that method, message can be sent in the following way:

Okay then, and what about tests itself? That’s quite simple, as we have our helpers:

Note – VerifyResponse method is for verification of the text and the resource file – it’s nothing to worry about.

Deployment slots

Okay, so right now we have nice IOC, so we don’t need to worry about dependencies, and in case of need we can easily replace implementation of any of the class that is requested by the constructor injection. We have some tests, that can be run to test basic functionalities of our bot. But what about real life tests? Are we limited to the separated instances?

No, we are not.

Very handy feature is Deployment Slots.

Deployment slots enables us to create multiple instances of the application, that can be easily, on the fly switched between. So for example – we can have test and production instance, both environments can be separated, and after tests on the test instance, instances can be swapped, so EXACTLY the code that has been tested is deployed to the production. But how to handle configuration in that case?

Another thing that I have refactored during implementation of IOC was configuration. As it was previously, and what is very common, many configuration elements are stored in the configuration files, whether it is web application and it’s web.config file, or if it’s function application and configuration is stored in settings.json. And for that purposes very handy solution is CloudConfigurationManager! It’s part of the Microsoft.Azure namespace, and it enables user to get configuration entry from config file, however! First place that it looks to is configuration stored on Azure. So during refactorization every instance of Configuration.GetSettings was changed go CloudConfigurationManager.GetSettings, and every call of that was moved to the class, where registration of the classes and interfaces were handled. So thanks to that I can be 100% sure, from what place value is obtained.

Going forward – thanks to that slot and configuration on azure you can create whole configuration, that will hide config from web.config. And what’s more – you can select, which one is „slot setting“, so it will persist even if you swap the instances. Isn’t that great?

So you can create whole different set of configuration, that can be pointing out to the different databases, Microsoft Apps etc, and swap that instantly either after tests, or for example in case of unexpected problems to roll back the code.

Okay, so the last thing remained – how can we deploy that easily?

VSO Build and Release Manager

Microsoft provides couple of ways to automate deployments. Thanks to that we can automate the whole process (so for example deployment can be carried on once a day, after commit/check in, or on any other condition). So, on VSO in our current project, in tab „Build and Release“ in „Builds“ subcategory you can create build definition. In our case it looks as follows:

Everything is rather self-explanatory. First – nuget packages are restored, then – whole solution is built. After that unit tests are run, and lastly – artifacts (build results) are published, so it can be accessed in next step – release.

In Release tab you can build flow of the deployment. In our case – it’s deployment to two environments – test and prod:

And in that steps, there are two tasks – to deploy web application, and second – to deploy Azure Function. Everything at once:

And after that configuration, it can be easily deployed by single click:

What’s more – you can always take a look for the details of the deployment, or – deploy only to singe environment if needed.

Also – you can also keep track of current version of the app, simply by looking at overview of the environments:

Having that done you have full control of the deployment, and you application overall. Good luck!

Microsoft Teams Bot – How to achieve the impossible? – Part 1
Microsoft Teams Bot – How to achieve the impossible? – Part 2
Microsoft Teams Bot – How to achieve the impossible? – Part 3