Automated Solutions

Automation Engineer to the Core!

"Test Automation is the key to dependable mobile and web applications. I make it my business to have computers work for us and not the other way around."

Understanding State with Elixir Agents

I mentioned a few blog posts back how managing state during the process of programming can be a very difficult task. Next generation programming languages are now starting to build in standardized approaches to solving this state change problem in a more elegant way. I've found myself doing more and more Elixir lately and I am sold on the concept of Agents. I get to write waaaay less code to take an object, or a piece of data through different states without the hassle of figuring out the best way to do it. This blog post will show a quick example of what I mean.

A most common example of something that changes state all the time is the bank account! Bank account state change can be for better or for worse. Modeling a Bank account in Elixir is a great way to show how simple this state management process is, and I will use the Agent module to illustrate this.

For a basic Bank account we are going to need 5 functions. These functions are

  1. open - Opens the bank account
  2. close - Closes Bank Account
  3. withdraw - Takes money from bank account
  4. deposit - Adds money to bank account
  5. balance - Gives current balance of bank account

In an effort to not get too complicated I'll start with these. 

 -Starting with a Module named BankAccount.  - Open function Calls the Agent.Start/2 function giving it a function that returns the starting value of 0, as well as the name of the current module

-Starting with a Module named BankAccount.

- Open function Calls the Agent.Start/2 function giving it a function that returns the starting value of 0, as well as the name of the current module

I like to always give my processes names because I don't like manually passing around a pid to all the functions I want to use. I am after all an automation engineer, so wherever I can find a shortcut I will!! Lets write the close function!

 -Puts a message to the user to let them know the accounts being closed  - Then calls the Agent.stop/3 function which terminates a running agent. __MODULE__ references the current module & in this case it is BankAccount. The second param :normal is an atom that tells the process which mode it should shutdown in. Because its not blowing up, and the close will be requested by the user of the module it can be set as normal. The last Param is defaulted to :infinity and doesn't need to be there, but I listed it anyway so that it is clear what timeout option I'm looking for.

-Puts a message to the user to let them know the accounts being closed

- Then calls the Agent.stop/3 function which terminates a running agent. __MODULE__ references the current module & in this case it is BankAccount. The second param :normal is an atom that tells the process which mode it should shutdown in. Because its not blowing up, and the close will be requested by the user of the module it can be set as normal. The last Param is defaulted to :infinity and doesn't need to be there, but I listed it anyway so that it is clear what timeout option I'm looking for.

Its always necessary to have a way to gracefully stop a process and thats the whole point of the function above. In this case a user might need to close their account for whatever reason. This is a quick way to do it. Lets actually increase our net worth by building our deposit function.

 -deposit takes one argument called amount.   -We then call out to Agent.update/3 and tell the agent we want to update the current state + the amount given on the BankAccount module

-deposit takes one argument called amount. 

-We then call out to Agent.update/3 and tell the agent we want to update the current state + the amount given on the BankAccount module

I absolutely love function guards in Elixir. It saves me from having to worry about having too much logic in the function body that guards against bad parameters. In this case, we want to make sure the amount given is an integer. Its important to note as well, that it doesn't make a whole lot of sense to make a deposit less than $1, so its also smart to add another guard against bogus negative amounts being entered. In a perfect world we would be done! Our bank accounts would just increase in value over time given the way our BankAccount currently works, but unfortunately, we have to provide a way for users to take away from our account (BILLS, BILLS, BILLS).  Let us painfully write this function next.

 -once again we pass an amount but we need to make sure its more than 1. It doesn't make sense to allow any other type of value below 1  -Again we call on the Agent.update/3 function that takes the current state - the amount given by the user of the function.

-once again we pass an amount but we need to make sure its more than 1. It doesn't make sense to allow any other type of value below 1

-Again we call on the Agent.update/3 function that takes the current state - the amount given by the user of the function.

In my opinion this is a good use case for the if statement in Elixir. I am aware that you could do pattern matching to clean this function up, but I usually only resort to pattern matching when I'm expecting more than 2 possible decisions a program needs to make. In this case you either have enough money in the bank to make a withdrawal, or you don't. If you don't have money I want to raise an exception. This exception is important because if we ever wanted to add a supervisor for this BankAccount process, the reason for a sudden failure of the BankAccount would be made absolutely clear in the log output. Now we don't want to leave the balance of our account a mystery, so we need a way to display the balance. Lets finish up this module with the final function.

 - Once Again relying on the Agent.get/3 to show the current state of the BankAccount Module and binding that value to current_balance reference  - The last step is to take the current balance and display it to the user

- Once Again relying on the Agent.get/3 to show the current state of the BankAccount Module and binding that value to current_balance reference

- The last step is to take the current balance and display it to the user

Bill Gates utilizing this module would look something like this....

 - Module interaction

- Module interaction

The output should look like this below:

 -BankAccount output

-BankAccount output

This is pretty cool because the state is tracked on its own by the agent. All you have to do is give it values. When I first started understanding the true reasons for Agents, I began to see so many use cases for them, especially for qa automation. State can now be managed independently of normal program flow which is HUGE! In languages that don't have state management carefully designed into them you would not be able to scale that program properly without major bugs and issues cropping up that you had no idea were present. Both Elixir & Clojure side step that nightmare by creating a standard way to handle state manipulation through Agents. This way, you don't have to worry about writing tons of code to track and manage state yourself, or exposing too many other objects with details of how and why to update a certain state. This is a huge win for programming, and actually makes it much more pleasurable than it used to be.