Showing posts with label Extensible Chaos Monkey. Show all posts
Showing posts with label Extensible Chaos Monkey. Show all posts

Tuesday, May 26, 2015

Moq - How to know the number of times a method is called on mocked object

Moq seems a good mocking library for .Net unit tests. It has most of the features a developer needs and pretty much stable and many people are using it. Once I started using it, I never felt a need to another mocking library. Below is one if the scenario which I had to face related to Moq.

Scenario

There is a method which randomly executes operations (IOperation implemented classes) stored in an array. The operations are unit tested independently to make sure there is no problems with them. This method which is in test simply uses Random class to get random number and get an operation from array using that number and invokes the operation.

Ideally speaking we don't need to test the Random class which is part of .Net framework. But why should we avoid unit testing our logic inside the method ? Some tests like 'making sure random value is not above the length of array' etc... So whats the big deal?

Challenge

The unit tests works by Arrange,Act,Assert methodology. First we need to arrange the things, Then act or do the operation by calling the method under test. Finally assert.

When we do the act, we should be knowing what is the output. Then only we can write assert. If we don't know what is going to happen we don't know what to assert.

An Example

Recently I forked ChaosMonkey for .Net and started contributing to it. I made it configurable. Looking into that code might be useful to understand the example better.It contains a MonkeyKeeper and IMonkey interface. MonkeyKeeper is the external interface. When MonkeyKeeper.Unleash() is called it should randomly pick an IMonkey from its monkeys collection and call Unleash() method on it. IMonkey implementations can be easily unit tested to make sure they are doing their duties on Unleash. But how to unit test MonkeyKeeper.Unleash()?

In this scenario, we can assert that the mocked IMonkey objects at least got one call to unleash. But in order to get at least one random calls, we need to call the MonkeyKeeper.Unleash multiple times because Random can return same value multiple times.

Below is one implementation, where MonkeyKeeper.Unleash() is called 5 times which is holding 3 IMonkeys collection.

[TestMethod]
        public void WhenMonkeyListProviderGivesMultipleMonkeysAndUnleashMultipleTimes_UseThoseMonkeysAtleastOnce()
        {
            Settings settings = new Settings();
            ChaosLogger logger = new ChaosLogger("");
        //Arrange
            var mockMonkey1 = new Mock<ParentMonkey>(settings, logger);
            var mockMonkey2 = new Mock<ParentMonkey>(settings, logger);
            var mockMonkey3 = new Mock<ParentMonkey>(settings, logger);
 
            var mockMonkeyListBuilder = new Mock<MonkeyListBuilder>();
 
            mockMonkeyListBuilder.Setup(builder => builder.GetMonkeys(settings, logger))
                    .Returns(new List<ParentMonkey>() {
                        mockMonkey1.Object,
                        mockMonkey2.Object,
                        mockMonkey3.Object
                    });
 
            MonkeyKeeper keeper = new MonkeyKeeper(
                settings,
                logger,
                mockMonkeyListBuilder.Object);
            //Act
            keeper.UnleashRandomMonkey();
            keeper.UnleashRandomMonkey();
            keeper.UnleashRandomMonkey();
            keeper.UnleashRandomMonkey();
            keeper.UnleashRandomMonkey();
            //Assert
            mockMonkey1.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
            mockMonkey2.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
            mockMonkey3.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
        }

Most of the times this test will succeed. But there is no guarantee that it will succeed always because Randon.Next() using inside MonkeyKeeper.Unleash can return same value 4 times and that's why its random. in such scenarios this test will fail. So whats the solution.

Solution

There is no solution to this. If we can predict random number and write asserts, that is not random. So what we can do?

At least make sure, there is some level of randomness in the selection of IMonkeys. Also we can make sure if there are 3 IMonkeys in the collection at least one of the IMonkey's unleash is called. If that is not called or called multiple times, there are some problems in the random selection logic of MonkeyKeeper.Unleash. Whats the problem in doing that?

We have 3 monkeys and we don't know which one will be selected by random logic. But we need to make sure that the unleash is called only once and on one monkey. This clearly tells that normal mechanism of asserting via Verify() and Times will not work. If there is any confusion just try writing that as below

mockMonkey1.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
mockMonkey2.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
mockMonkey3.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);

Why the above wont work? when we unleash it may call mockMonkey1. and that line works and test passed. But what about the next lines? They will fail. We cannot put a return statement because next time mockMonket3 might be the one selected. In that case first 2 statements will fail.

Now whats the method? We need to know the number of times a method is called on mocked object. There is no direct way to know that. So we need to use the callback mechanism available in Moq to achieve our aim. Callback function helps to get a hook in our unit test when a particular method is called on mock object. Code goes as below.
[TestMethod]
public void WhenMonkeyListProviderGives3MonkeysAndUnleashOnce_UnleashMustBeCalledOnOneMonkey()
{
    Settings settings = new Settings();
    ChaosLogger logger = new ChaosLogger("");
    //Arrange
    var mockMonkey1 = new Mock<ParentMonkey>(settings, logger);
    var mockMonkey2 = new Mock<ParentMonkey>(settings, logger);
    var mockMonkey3 = new Mock<ParentMonkey>(settings, logger);
 
    int callCount = 0;
    mockMonkey1.Setup(monkey => monkey.Unleash()).Callback(() =>callCount+=1);
    mockMonkey2.Setup(monkey => monkey.Unleash()).Callback(() => callCount += 1);
    mockMonkey3.Setup(monkey => monkey.Unleash()).Callback(() => callCount += 1);
 
    var mockMonkeyListBuilder = new Mock<MonkeyListBuilder>();
    mockMonkeyListBuilder.Setup(builder => builder.GetMonkeys(settings, logger))
            .Returns(new List<ParentMonkey>() {
                mockMonkey1.Object,
                mockMonkey2.Object,
                mockMonkey3.Object
            });
 
    MonkeyKeeper keeper = new MonkeyKeeper(
        settings,
        logger,
        mockMonkeyListBuilder.Object);
    //Act
    keeper.UnleashRandomMonkey();
    //Assert
    Assert.AreEqual(1, callCount, "Unleash Called multiple times");
}

http://stackoverflow.com/questions/16693078/when-using-moq-verify-method-invocation-count-have-failing-tests-error-messa

Tuesday, May 5, 2015

Extensible Chaos Monkey

Chaos Monkey

Chaos Monkey is a great concept introduced by Netflix to create random issues in cloud server environments so that those issues can be addressed early and test against unexpected failures. That makes sure any system can recover from any kind of failure which can happen in production.

The good news is that they didn't keep it secret. They made that tool online including source in Github. It is written in Java so it was a little difficult for the .Net community to extend. There are many .Net ports of the same tool or tried to implement the same.

The problem

In our company also things are not different from any other production environment. There are issues which are first reported / only happens in production. We have an online audit system that contains many backend processing components. The backend queueing system is developed in such a way that any application track developer can add a new queue type and write it's associated handler. Though there are guidelines for the backend developers to roll back properly, there are times when developers are not caring it to meet the deadlines and it goes to production. In production, we end up with the wrong data states. Some of the reasons are unexpected IIS / AppPool recycling. Database timeout/outage etc...Since the application is not expecting those states, it will be clueless about how to recover from there. Finally, support has to manually run SQL statements in production servers to correct the state.

Ideally, QA is supposed to test scenarios such as IIS reset and all when the backend services are running. But they have limitations. It is very difficult to make sure that they cover all the scenarios.  Ensuring the randomness and tracing the abnormal event to reproduce in dev will be difficult. There are also difficulties in manually creating abnormal scenarios when the tests are running overnight.

Solution

All these things lead to a testing strategy where issues / abnormal scenarios that are expected in production needs to created in dev/QA environments so that we can identify how the system behaves to those abnormal events and sometimes change the application flow to recover those states.

This is the point we started looking towards already existing ChaosMonkey solution. It seems suitable for us. But we are not able to use it straight away because ChaosMonkey is targeted towards cloud but our production deployment is in house.

Why extensible

So we tried to extend the tool. But since its in Java and most of our developers are familiar with .Net ecosystem, we started looking for .Net port of Chaos Monkey. Unfortunately not able to find much.  Though there are some they are also targeted towards the cloud. To restart the local IIS server, had to write a lot of code. 

So finally decided to take one as a base and make it extensible to meet the scenario. Forked ChaosMonkey implementation from Githb by Simonmnro and added my own changes to make it pluggable.

My version of Extensible chaos monkey is available in the below location.

What's Next

  • Clean separation of plugin code and config
  • Adding more plugins such as increase memory pressure.
Feel free to comment if there is a better solution to the issue and contact me if willing to contribute to the project.