Prashanth Batchu

Improve Imperative design in control statements with a Functional Design

Imperative code is hard to read, forces the designer to mutate state and is a breeding ground for hard to find bugs. But why are Programmers attracted to writing in an “Imperative style”? May be because that is because they started their careers in procedural and object oriented languages and Imperative style comes natural to them. Imperative design coupled with the principles of Functional programming can go a long way in simplifying t

“Perfection is achieved when there is nothing left to take away.”

 Antoine de Saint-Exupéry

In Software Design, perfection and simplicity go hand in hand. Writing in Imperative style can be faster but building the necessary abstractions to write following a Declarative style takes time and patience. And we all seem to lack these virtues these days! Here’s another quote that’s apt for this situation

“I apologize for such a long letter – I didn’t have time to write a short one.”

Mark Twain

Let the language do the heavy lifting

When you mutate the state, you are responsible for making sure that you don’t introduce any bugs in your design. However if you embrace a declarative approach, you let the language do the heavy lifting and avoid introducing bugs.

Declarative style is more readable for half the work

Not only a Declarative style more readable, but you can accomplish the same work with fewer lines of code.

Functional programming makes you write better code even when you write in pure OOP

You may be forced to write in a non Functional language for various reasons. But learning to code in a Declarative style or using a Functional programming language can help you borrow the principles (Data immutability, Pure functions) and help you write better code no matter in which language you have to code in.

Here’s an example in Kotlin that illustrates how bad imperative style of programming can be.

//A bad solution
var i = maxPerRequest
var c = 2
while (i < searchForIssues.total && i < total) {

    log.info("Request #$c Quering API jql $jql, for issues $i to ${i + maxPerRequest} out of ${searchForIssues.total}. " +
            "Total limit is set to $total entries")
    val searchIssues = jira.searchIssues(jql, null, maxPerRequest, i)
    jiraQaps.addAll(searchIssues.issues)
    log.info("Waiting $pauseInterval miliseconds before making next call")
    c++
    i += maxPerRequest
    if(i < searchForIssues.total && i < total)
        Thread.sleep(pauseInterval.toLong())
}

Fortunately, Kotlin provides constructs that can help clean this up. Here’s one way to remove imperative elements and make it more readable.

//A better solution
val limit = if( searchForIssues.total < total)  searchForIssues.total else total

 for (i in maxPerRequest until limit step maxPerRequest) {
     log.info("Quering API jql $jql, for issues $i to ${i + maxPerRequest} out of ${searchForIssues.total}. " +
             "Total limit is set to $total entries")
     val searchIssues = jira.searchIssues(jql, null, maxPerRequest, i)
     jiraQaps.addAll(searchIssues.issues)
     log.info("Waiting $pauseInterval miliseconds before making next call")
     Thread.sleep(pauseInterval.toLong())
 }

Notice that there are no more variables in the above implementation. It’s more readable, conveys the intent to the reader so it makes it easier to maintain or change. Kotlin makes it easy to make something final unlike Java. This is important for psychological reasons. People tend to follow best practices when you make it easier for them to do so! Who likes to type in “public static final……”?

CIOSynergy 2020 – IT trends & predictions

I had a wonderful time at the CIOSynergy 2019 Meet in New York, NY. Here are some highlights and key takeaways from the event.

The #Predictionspanel

CIOs must manage not only for today, but for tomorrow, and for years into the future. This implies a constant job of predicting what is going to happen next — at your company, in technology advancements, in competitive marketplace conditions, and more. 

CIOSynergy

Panelists

Key takeaways from the 2020 panel discussion

3D Printing Revolution

3D printing has so far allowed customers to create re-prints of certain artifacts controlled by the Manufacturer. But the next evolution (and revolution) in this area is the transfer of “Capabilities” and “Solutions” from the Manufacturers to the Customers. This allows customers to innovate and build new products independently. Rapid Advances in the Material sciences industry are going to revolutionize the Manufacturing and the IT industries – Ajay Kamble, CIO, Turtle & Hughes, Inc.

VICE’s Krishna Andavolu delves into the cutting-edge research behind what’s being called the next industrial revolution, meeting the scientists and entrepreneurs pushing the boundaries of manufacturing, material science, and even space exploration.

Threats of Ransomware sophistication due to A.I & M.L

Evolution of Ransomware threats in terms of sophistication that take advantage of Artificial Intelligence and Machine Learning is going to continue to make it a challenging environment for business of all sizes and shapes – Dave Smithers, CIO, IDB

The #CIOpanel

Panelists

Panelists shared their challenges and opened it up to a group of 50+ CIOs in the audience for a meaningful dialogue.

Centralizing IT Operations

A Healthcare company facing challenges in centralizing IT operations which were historically localized (by facility i.e Hospital, Clinic etc.).

Challenges

  • Centralizing implies changing the IT support structures that aren’t welcome by Doctors as they fear losing access to in person support.
  • Each localized facility has their own policies and governance. This not only poses Information Security risks but also increases overall operating expenses due to duplication of resources & inefficiencies due to lack of centralization
  • IT staff perceive efforts to centralization negatively as they will be forced to change the way they do their work. They may be forced to move to a different location (as the IT org restructures due to centralization), switch to a different role based on need etc.
  • Difficult to measure performance, organize/pool resources for large initiatives and general operating inefficiencies

Solutions

Here are some strategies I can propose based on my professional experiences that the CIO can adapt to tackle these challenges

Communication

The greatest barrier to communication is the illusion that it has been achieved,”

Dr. Galdston

Creating a strategy for an organization is often sometimes the easiest thing to do compared to its implementation. As Helmuth van Moltkea , a Prussian military commander once said “The best laid plans never survive first contact”, figuring out how to realize the strategy and influence the masses in the organization to follow through can turn in to an impossible endeavor. One of the biggest challenges is Communication. A strategy is only great for those who understand and believe in it. So if a CIO hasn’t managed to sell the strategy to the employees, the employees may not see the value in it. They need to have a vested interest in the strategy and believe in it.

Organizing Quarterly Townhalls for the entire IT organization (including stakeholders) and clearly presenting the strategy, recognizing accomplishments of high achievers, reviewing the challenges with the status quo, allowing employees to ask questions and valuing their participation can be a very powerful way to get their buy in. My current boss, Wilson Lukang, The Head of GWM Quality Engineering at UBS has pioneered this idea. Witnessing the effectiveness of this strategy, the GWM CIO has begun doing his own Quarterly Townhall! It’s important for leaders to address the entire Organization that they lead. It’s also important for the leaders to hear directly from their employees and address tough questions.

Promoting a culture of Learning, Respect and Innovation

Support, not challenge each other

  • Team members should be helping each other. Simply challenging team members every time undermines their efforts and can be demoralizing

Know that your team members are people, with emotions, aspirations and goals. If you treat them with respect and give them the benefit of doubt, they will return the courtesy and it leads to a positive learning environment.

Instead of saying

“You said this would work, but it doesn’t”

rephrase it as

“Looks like it doesn’t appear to work. Can we find out what’s going on and can I help?”

Aim for progress, not perfection

Just like most things in life are not black and white, but somewhere in the gray area, in between, most projects can never be perfect. There’s nothing wrong in aiming for perfection, but understand that progress made by your team members needs to be recognized, just as much as investing time in areas that do not appear to succeed.

Incentivize communication about the bad and the ugly

If you always want your teams to bring good news to you and you never want to hear them complain about the bad and the ugly, by design your teams will hide all the bad and the ugly and become bearers of only good news. This hides potential problems that you need to know before those problems come back to bite you.

Supercharge Software Testing with On Demand Jenkins Agents and Selenium Grid with Kubenetes

Maintaining a Stable Continuous Integration environment the traditional way requires a lot of work. It involves building an Army of Jenkins Agents, configuring them in a certain way (SDKs, Build and Runtime configuration etc.).

This is further complicated if you need to setup a platform for User Acceptance Testing (or Functional) which includes adding a Selenium Grid to your environment including the Hub and one or more Agents. And you will need to test against multiple Browsers (Chrome, Firefox, IE) and multiple versions of them. Companies such as Perfecto, Sauce Labs and BrowserStack offer cloud based testing solutions. But if you an on premises solutions within the Enterprise, Jenkins Kubernetes plugin provides an elegant solution.

Here’s what we will accomplish with this tutorial

  • Install Minikube
  • Helm & Tiller
  • Jenkins
    • Kubernetes plugin
  • Jenkins pipeline project
    • Create an on Demand Jenkins Agent*
    • Selenium Grid & Selenium Nodes for testing*

*Resources are destroyed after use.

Prerequisites

  • Windows 7/10/Linux/MacOS
  • Chocolatey for Windows or an equivalent package manager such as Brew, APT etc.

Install Minikube

On Windows,

choco install minikube

Follow this guide for instructions on other Operating Systems

Run Minikube

minikube start --cpus 4 --memory 8192

Run the Minikube Dashboard

minikube dashboard

The dashboard provides a GUI to view and manage Kubernetes resources.

Setup Networking for Minikube

minikube tunnel

Install and Initialize Helm & Tiller

choco install kubernetes-helm

For other OSes, follow these instructions

Install Jenkins

Save the following as jenkins.yaml

 master:
  installPlugins:
    - kubernetes:1.15.5
    - htmlpublisher:1.18
    - workflow-aggregator:2.6
    - workflow-job:2.32
    - credentials-binding:1.18
    - git:3.12.0
    - greenballs:1.15
    - role-strategy:2.9.0
    - locale:1.4 
 helm install --name jenkins -f jenkins.yaml stable/jenkins

Jenkins login credentials can be found on the Pod description page on the Minikube Dashboard

Jenkins IP address can be found in the “Services” tab.

Setup a Jenkins Pipeline Project

Login to Jenkins and create a new Pipeline project

Add the following the the “Script” section


Build the project. Jenkins will create a temporary (dynamic) agent including an instance of Selenium Hub and each instances of Chrome and Firefox.

Lessons in Leadership from History

When we think of great leaders, luminaries such as Abraham Lincoln, Franklin Roosevelt, Winston Churchill or Dalai Lama come to our minds. But what makes them great? Do their lives offer lessons in to leadership? How well do these lessons translate to the Board room? Fareed Zakaria interviews Pulitzer prize winner Doris Goodwin, Rep. John Lewis, Gen. Stanley McChrystal and Bill Gates on his show Global Public Square on CNN (Aired on 7/7/2019).

Empathy

President Franklin Roosevelt came from a very wealthy family. He contracted Polio at the age of 39. This debilitating disease had a significant impact on Roosevelt’s leadership style and the humility that came with it enabled him to understand the plight of common man for whom fate had dealt an unkind hand (vis-à-vis the Great Depression).

When he had visitors at the White House, he would know who they were, he knew how to talk to them, get them relaxed,talk about their families and by the end of the evening, he would get to know everything about the issues that were important to them.

For leaders, decision making without empathy can be disastrous. The original responses to the two recent Boeing 737 Max accidents from Boeing CEO Dennis Muilenburg did not empathize with the plight of the families who have lost their loved ones. Boeing went on to great lengths in denying any knowledge of safety issues and defending their safety record. Boeing suffered the consequences for not showing empathy, humility and for their poor crisis management skills. Their share value dropped by more than 20% following the aftermath. It is yet to receive any new orders for the 737 Max.

Self Restraint

Abraham Lincoln became very frustrated with General Meade when he failed to pursue and destroy General Lee’s Confederate Army against his advise. That could have ended the war. So Lincoln drafted a brutal letter criticizing the actions of the General. Here’s a snippet from that letter

I do not believe you appreciate the magnitude of the misfortune involved in Lee’s escape– He was within your easy grasp, and to have closed upon him would, in connection with the our other late successes, have ended the war– As it is, the war will be prolonged indefinitely. If you could not safely attack Lee last Monday, how can you possibly do so South of the river, when you can take with you very few more then two thirds of the force you then had in hand? It would be unreasonable to expect, and I do not expect you can now effect much. Your golden opportunity is gone, and I am distressed immeasurably because of it–

I beg you will not consider this a prosecution, or persecution of yourself– As you had learned that I was dissatisfied, I have thought it best to kindly tell you why.

Abraham Lincoln to George G. Meade, Tuesday, July 14, 1863 (Meade’s failure to pursue Lee)

But Abraham Lincoln never sent this letter out. He knew that it would devastate the General. Lincoln leveraged this strategy to calm him tempers and not let his judgment overcome his emotions. This letter was never made public in his life time and had was annotated with the phrase “To Gen. Meade, Never sent, or signed.”

One of Franklin Roosevelt’s initial drafts of his famous Fireside chats start with lambasting an isolationist congressman, naming him and calling him a traitor. A young speech writer concerned by the draft was then assured by a senior to just wait for the next set of drafts. By the second draft, the congressman’s name was taken out. By the third draft, the congressman’s portrayal wasn’t bad anymore. By the final draft everything is sweetness in life.

Leaders aren’t immune to emotions but they have developed a set of tools to manage them and not let their emotions get the best of them.

Pulitzer prize-winning historian Doris Goodwin, Rep. John Lewis, Gen. Stanley McChrystal and Bill Gates on leadership with Fareed Zakaria GPS (July 7 2019)

Project Management: Right tool for the right Job

No battle plan ever survives contact with the enemy” – Helmuth von Moltke, a 19th-century head of the Prussian army, as quoted in Donnybrook : The Battle of Bull Run, 1861.

The same goes for Project Management. Most projects fail as soon as they come in contact with something called ‘humans’. We humans are emotional creatures. The reason for failure isn’t necessarily a lack of ability or lack of will but mainly because of not planning the human element in to the plans. Let me rephrase: Does your Project plan include review or assurance of Psychological Safety, Dependability, Structure & clarity, Meaning of Work and Impact of work? These five aspects come out of a research project’s findings from Google about why some teams succeed while most fail. I’m sure that if you send me your Project Plan arguing that it includes “Structure & Clarity”, I’d reply back saying Yes and No. Just because it is included doesn’t necessarily mean that it is described effectively. And if the Business and Development teams use JIRA or any other bug tracking tool to carve out and manage hand off of work then you might need to re-think your workflows. Bug tracking tools can be the worst tool of choice because

  • Requirements change continuously as work progresses
  • Requirements may end up being found unreasonable if there’s no implementation path or if there is no appetite cost of such implementation
  • Division or organization of work in to individual JIRA issues or stories is completely subjective and may not be straightforward when they are handed off to a different team (Say the Business teams divide the work in to JIRA issues and hand it off to Development and then they transfer it further to QA after DEV complete)
  • Granularity of Division may also be very subjective. One developer might want to create a JIRA issue for every URL mapping (endpoint) of a REST Service plus separate while another developer might want to track everything related to the controller in just one.
  • When filling a JIRA issue there’s a section for filling in Estimates. Treating these Estimates as SLAs or concrete expectations are a recipe for disaster. These estimates should be treated as guesses. If the work is not complete within the duration of the estimate it should not be counted against the assignee. These estimates should not be considered for evaluating performance.
  • Bug tracking tools are good for tracking bugs and reporting but are not ideal for Project Management because you cannot account for the five aspects (outcomes of Project Aristotle discussed above).
  • Semantic Diffusion: The team members are out of sync when it comes to their understanding or their philosophy towards Agile, Project Management and pretty much every aspect of Project management including but not limited to Estimation, Purpose of using JIRA for Software Development, Workflow of SDLC through JIRA.

How do the five aspects as laid out by Project Aristotle apply to usage of JIRA and Project Management in your team?

  1. Psychological safety: Can the individual team members debate or be allowed to openly question any aspect of the way JIRA is used in your team that does not make sense to them or is inefficient? Are team members encouraged to speak up during the planning or the Sprint review meetings?
  2. Dependability: Are the correct expectations of “What constitutes a quality deliverable” conveyed to the team members? Do the team members know that the team can count on him/her for the delivery with the quality expectations set by the team? Does the team have quality expectations? (Such as unit test coverage, Complexity requirements, Architecture guidance, collaboration help when dealing with complex problems or for brainstorming)
  3. Structure & clarity: Are goals, roles, and execution plans on our team clear for each team member for iteration or sprint?
  4. Meaning of work: Are we working on something that is personally important for each of us or are chunks of work randomly assigned to team members without rhyme or reason? What’s the formula that is used for carving up work and how do they help achieve this criterion?
  5. Impact of work: Do we fundamentally believe that the work we’re doing matters? Is anybody doing any analysis of how the work being performed in this integration is aligned with the Business? After all the entire goal of I.T is business alignment.

It’s time for your team to get together and have an open conversation about how well the tool you use for Project management is serving your needs.

Beginning development with ReactJS & TypeScript (Part-2)

This post’s focus is on testing the components for Tic-Tac-Toe game created in Part-1

Setup

  • Checkout the tag  step-4
  • Pull the new dependencies for this step by simply running yarn at the root of the project

Dependencies needed for Testing

  • Jest, a JS testing framework from Facebook but widely used for testing React applications
  • Enzyme from Airbnb which makes it easier to traverse through React Component and helps interact with the components (Issue events like clicking on a button, finding an element for asserting it’s value, etc)

Update the package.json with these additional dev dependencies and their corresponding type definition packages.

"devDependencies": {
"enzyme": "^3.2.0",
"enzyme-adapter-react-16": "^1.1.0",
"react-test-renderer": "",
"@types/enzyme": "^3.1.4",
"@types/jest": "^21.1.2",
"@types/react-test-renderer": "",
"@types/enzyme-adapter-react-16": ""
}

Export the components so that they can be imported in to the tests (MDN’s primer on ES6 exports)

Add the export statements for the Game, Board and Square components. ‘App’ component is the default export of App.tsx (The class declaration starts with ‘export default’)


export { Game }
export { Board }
export { Square }

Let’s start with Testing the components! Here is the final App.test.tsx with the tests beings reviewed below.

VsCode Setup for debugging Jest tests with TypeScript using React

Open up the launch.json and add the following debug config (Credits)


"configurations": [
{
"name": "Debug Jest Tests",
"type": "node",
"request": "launch",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts-ts",
"runtimeArgs": [
"--inspect-brk",
"test"
],
"args": [
"--runInBand",
"--no-cache",
"--env=jsdom"
],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}

Now you can put the break points as needed and hit F5 to begin a debug session.

Import the necessary dependencies


import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as enzyme from 'enzyme';
import * as ReactTestRenderer from 'react-test-renderer';
import * as Adapter from 'enzyme-adapter-react-16';
import App from './App'
import { Board, Game, Square } from './App';

Configure Enzyme once before running the tests


beforeAll(() => {
    enzyme.configure({ adapter: new Adapter() })
})

The test for the App component in App.test.tsx is pretty basic and uninteresting. It just verifies if the entire app loads without any issues. The Game component is subjected to a Snapshot test which is also pretty straight forward.

Here’s a basic test for the Board component to check that it loads 9 squares, no more and no less. We use enzyme’s shallow method to render the Board Component and find the number of the instances of the ‘Square’ components that it loads.


describe('', () => {
    it('should render three  components', () => {
        const wrapper = enzyme.shallow()
        expect(wrapper.find('Square').length).toEqual(9)
    })
})

Let’s look a test for the Square component. The component accepts two inputs

  • An anonymous function that should be invoked during a click event
  • The value to be displayed after the button is clicked.

So we begin by creating a mock function by invoking jest.fn() and create a constant squareValue.  We pass these to the Square component while rendering it with Enzyme. A button click is now issued through Enzyme. We can now verify if the mock function is called once and if the square displays the value ‘X’.


describe('', () => {
    it('when clicked it should invoke the function attached to the click event', () => {  //Create the necessary input values for the test case const squareValue = 'X' const mockOnClick = jest.fn()  const wrapper = enzyme.shallow()  wrapper.find('button').simulate('click') expect(wrapper.find('button').props().children).toEqual('X') expect(mockOnClick.mock.calls.length).toBe(1) 
     })
})

I’ve done extensive development in Angular 2 using initial releases of VSCode in early 2016. It looked very promising but I had to switch to Intellij/WebStorm as VSCode lacked refactoring capabilities such as extracting methods from a set of lines, refactoring variable names with ease and some other features. But it has come a long way since then.

Beginning development with ReactJS & TypeScript (Part-1)

The official tutorial from React does a pretty good job of introducing React to a Beginner through building Tic-Tac-Toe. But it’s incomplete as it doesn’t put any focus on Unit Testing and provides no introduction of Typing through TypeScript or Flow, which can be very beneficial in the course of any non-trivial app development. The purpose of this post is to add TypeScript to the mix and also

  • Retain Livereload features that come with the out-of-the-box starter template through the ‘create-react-app’ Node module
  • Gain from Typing: Receive live feedback of Transpile time checks to ensure that typing contracts are met. This helps immensely with discovering bugs early on.
  • Introduce Unit Tests and focus on various ways components can be (and should be) tested.
  • Considerations for Components to properly document the typings of any newly introduced variables and method i.e the Component Contracts.

I highly recommend completing the official tutorial from React before continuing this tutorial.
The Source code for this tutorial can be found here. It’s based on Microsoft’s own starter template for TypeScript & React.

Setup

  • Clone the repo
  • git clone https://github.com/batchu/Tic-Tac-Toe.git
  • The Project is tagged at different stages starting with ‘step-0’. Let’s start with step-0 which includes the starter setup, basic components and the necessary configuration needed to begin our development.
  • git checkout step-0
  • From here (step-0) you can create your own custom branch to start making commits.
  • Git checkout -b some_branch_name
  • Alternatively If you’re using Intellij, Simply open the project, Click on Run -> Edit Configurations and Add a new item under ‘npm’. Fill in the location of ‘package.json’, select ‘run-script’ under ‘Command’ and type in ‘start’ within the ‘Scripts’ field. Save the configuration and it will now be available for use on the top-right side of the screen.
  • It’s a good practice to take advantage of Git’s features by frequently committing, branching as needed to help organize your progress. You can always diff with the nearest “tag” version if you’re stuck to fix any problems. (Example: Say you’re at step-3 and run into an issue with your implementation. You may diff your current state with the tag step-4 to see the differences)
  • I will refer to Components by their name within this tutorial.(For Example, I’ll refer to the “Board Component” simply as the Board)

Introduction to the Project

Since we’re going to build a simple version of Tic-Tac-Toe, we will start with the following Component hierarchy

  • App -> Game -> Board -> Square(s)

For now, let’s maintain these components within App.tsx. Rendering begins with the index.tsx which renders the App (Component). The Game (Component) doesn’t do anything interesting at this point other than loading the Board. But this will change in the future.

The Board renders Nine (9) Squares through each by calling the renderSquare() method.

The renderSquare() method passes the value to be displayed to the Square. Notice the type of input param to the renderSquare method. This helps us ensure that the right type of value is passed to the method.

class Board extends React.Component {

    renderSquare(i: number) {
        return (
            <Square value={i} />
        );
    }
    render() {
        return (
            <div>
                <div className="status">{status}</div>
                <div className="board-row">
                    {this.renderSquare(0)}
                    {this.renderSquare(1)}
                    {this.renderSquare(2)}
                </div>
                <div className="board-row">
                    {this.renderSquare(3)}
                    {this.renderSquare(4)}
                    {this.renderSquare(5)}
                </div>
                <div className="board-row">
                    {this.renderSquare(6)}
                    {this.renderSquare(7)}
                    {this.renderSquare(8)}
                </div>
            </div>
        );
    }
}

The Square grabs this value from Props and displays it. We declare that the Square adheres to SquareProps TypeScript Interface and an Empty State Map ({}) when extending React.Component.

class Square extends React.Component <SquareProps, {}> {
    render() {
        return (
           <button className="square">
               {this.props.value}
           </button>
        );
    }
}

Since the attribute ‘value’ within props has been created by us, we need to declare it for type checking purposes within SquareProps.

 interface SquareProps {
 value: number;
 }

Try running it in it’s current state (npm run start) and you should see a grid with 9 squares filled with numbers from 0 to 8.

Let’s attach a click Event to the button within a Square to invoke an anonymous function and simply alerts a message (checkout step-1 tag to arrive at this state).

class Square extends React.Component <SquareProps, {}> {
    render() {
        return (
           <button
                className="square"
                onClick={() => {alert(`clicked on ${this.props.value}`); }}
           >
               {this.props.value}
           </button>
        );
    }
}

Now let’s refactor the Board by adding a constructor to hold a state variable ‘squares’ which stores the values of all the squares as an Array. Each value is retrieved and passed on to the Square. An Event Handler function is also added to the Board and is sent to the Square through props. This way the Square can simply call this function for handling the click event. We are basically passing the values and the behavior to the Square from it’s parent Board.

class Board extends React.Component <BoardProps, {}> {

    constructor() {
        super()
        this.state = {
            squares: Array(9).fill(null)
        }
    }

    handleClick(i:number) {
       alert(`Handling click in the Board Cmp now for ${i}`)
    }

    renderSquare(i:number) {
        return (
            <Square
                value={`${i}`}
                onClick={()=> this.handleClick(i)}
            />
        );
    }

    render() {
        return (
            <div>
                <div className="status">{status}</div>
                <div className="board-row">
                    {this.renderSquare(0)}
                    {this.renderSquare(1)}
                    {this.renderSquare(2)}
                </div>
                <div className="board-row">
                    {this.renderSquare(3)}
                    {this.renderSquare(4)}
                    {this.renderSquare(5)}
                </div>
                <div className="board-row">
                    {this.renderSquare(6)}
                    {this.renderSquare(7)}
                    {this.renderSquare(8)}
                </div>
            </div>
        );
    }
}

The Square needs to be modified to simply invoke the click handler method it received through props.

class Square extends React.Component <SquareProps, {}> {
    render() {
        return (
           <button className="square">
               {this.props.value}
           </button>
        );
    }
}

Since the “onClick()” method is a custom method, it needs to be declared in the SquareProps interface.

interface SquareProps {
     value:string;
     onClick:()=>void
 }

Now, let’s try to change the value of a clicked Square and store it in the squares state variable within the Board. (git checkout step-3).

Here are the updated handleClick() method. Notice that the squares is assigned a copy of the original squares stored in the state by slicing it. This is to ensure that the original object within the state remains unmodified. Data immutability goes a long way in improving the design, efficiency and reducing bugs caused by unintentional modifications of objects. A consequence of Data Immutability is the simplification of Multi Threading implementations. This basically avoids race conditions as the original Data objects remain unmodified. Each thread can happily do it’s processing without the need of complex and expensive Thread Synchronization techniques.

handleClick(i:number) {
         const squares = this.state.squares.slice();
         squares[i] = 'X';
         this.setState({squares: squares})
     }

Here’s the updated renderSquare method. The value and the onClick() method are passed through props to the Square component.

renderSquare(i:number) {
         return (
              this.handleClick(i)}
             />
         );
     }

Let’s look at the changes made for the Square. The onClick handler simply invokes the onClick() method it received through props from it’s parent Board. Note that this new “onClick” method within it’s props needs to be declared explicitly within the SquareProps interface. So it is amended.

interface SquareProps {
    value:string;
    onClick:()=>void
}
class Square extends React.Component <SquareProps, {}> {
    render() {
        return (
            <button
                className="square"
                onClick={() => this.props.onClick()}
            >
                {this.props.value}
            </button>
        );
    }
}

Let’s continue with the above implementation by writing the necessary Unit tests in Part-2 of this tutorial.

Introducing Best Practices such as Unit Tests and TypeScript to a Legacy Project built with a Pre ES6 Framework such as ExtJS

There are instances when Legacy JS projects are actively developed which are based off of   Pre-ES6 era frameworks such as ExtJS or even just plain vanilla ES5 or earlier. But that’s no excuse for not writing automated tests. There may not be an appetite for reaching optimal code coverage numbers for the entire code base, but at a minimum a developer must not deliver  new or modified code without accompanying Unit tests. It is always ideal to improve the  coverage on an existing code base if the project is in active development for any reason.

Delivery of Source code without accompanying automated tests to assure quality is incomplete

Introducing unit testing to a legacy project based on frameworks such as ExtJS (Sencha) or vanilla JS plus Typescript can improve  quality and speed up the development velocity. Typescript is a superscript of JavaScript. It can be safely introduced in to most JS projects without having to refactor existing code.

Benefits of introducing TypeScript to a JS project

  • Discover  typing errors during the Transpilation phase
    • Detect incorrect usages of objects and methods, non-conformance of method signatures and improper use of variable types.
  • Speed up the development by leveraging IDE code complete features or predict the possible usages of a variable, method or an Object due to the introduction of ‘Types’.

Tools needed to setup and run the tests

  • A tool that can bootstrap the Unit Tests. Karma & Ava fit this bill. Most projects use a Build/Bundler tool such as Webpack or Gulprun for various build tasks such as
    • Run the Typescript compiler for transpilation,
    • Invoke the execution of Unit Tests through  Karma or directly through Jasmine,
    • Convert SASS to CSS
    • Minify the sources and pretty much prepare the sources as needed for deployment.
  • A framework such as Jasmine to write the actual tests
  • A Continuous Integration system (Jenkins/Bamboo) for automatically running the build tasks and for optionally notifying the team on test failures  and for promoting builds. The goal is to fail the build when tests fail.

Steps

Basic Project setup

Assuming that you already have a code base, Just go to the root of your code base and run any commands listed below

  • Install Node.js
  • Install Yarn (This is quite superior and blazingly when fast compared to npm)
  • Initialize your project through Yarn by running “yarn init”
  • Add the dependencies (karma, jasmine & typescript)
    • yarn add package_name –dev
  • Install dependencies by simply running “yarn” at the root of the folder.

Setup Karma configuration

  • Create a folder (test) than can hold the  test cases and the test configuration
  • In the “test” folder, create a configuration file for karma titled “karma.conf.js
  • The order of the array in the “files” section is important. Here’s the proper order for this setup
    • Load any core frameworks being used (ExtJS in this instance)
    • Load any Test Libraries (Sometimes certain parts of the sources may be mocked. An example could be a Login module or a shared module that is needed by the application and you need fine grained controls so you prefer to create a test version of it instead of plainly mocking it using Jasmine)
    • Load the application sources
    • Load the test cases (specs)

Setup Typescript configuration

  • Create a tsconfig.json in the “test” folder. This can actually be moved to the root if typescript needs to be introduced to the entire project instead of just the test cases.

Write your tests (specs)

Let’s first talk about the application that needs to be tested. Clone this repository and browse to the “app/view” folder

  • This is a simple CRUD application for store real names of our favorite Super Heroes. (Run the index.html from the root of the application in the browser to check it out)
  • The HeroGrid.js is the primary view and holds the main Grid that displays an existing list of Super Hero names
  • It has a form in it’s header through which new entries can be added.
  • The Grid is backed by HeroGridController.js, (Controller) HeroGridModel.js (View Model), and  Hero.js (Model)

Testing the Grid Configuration

The Grid itself simply extends a standard ExtJS grid and  holds configuration (both display and component configuration) and declaration of the Controller method names that need to be triggered based on appropriate user actions.

At first glance, it may  appear that there isn’t much to test. But testing the display  and column configuration or even the title can be handy in certain situations. Here’s a simple spec for the same.

  • The describe() method takes two arguments. A description of the test suite and a function which includes the actual tests.
  • The actual tests are invoked by calling the it() method. The first param describes the behavior of the test case (Hence the term “Behavior Driven Driven” (BDD) tests attribution given to the tests written with Jasmine)
  • The beforeEach() method  is invoked every time before running. In our case we just re-initialize the this.cmp so that each test case has a fresh untouched instance  of the component to test.
  • “Ext.isDomReady = true” forces Ext to think that the DOM has been loaded in the Unit testing environment for it to work properly.
describe("Tests for Hero Grid Component", function(){
    let cmp
    beforeEach(function () {
        Ext.isDomReady = true
        this.cmp = Ext.create('SuperHeroes.view.HeroGrid')
    })

    it("Component height should be a fixed 500", function(){
        expect(this.cmp.height).toEqual(500)
    })

     it("Component title should be 'Super Heroes Grid'", function(){
        expect(this.cmp.title).toEqual("Super Heroes Grid")
    })

    it("Grid's first column must be the first name", function(){
        expect(this.cmp.columns[0].dataIndex).toEqual("first_name")
    })
})

Testing the Controller

The meat of the CRUD implementation is in  HeroGridController.js through the onAdd(), onUpdate() and onDelete() methods. Review the spec for the controller here.

Let’s break this down and go through the key elements of the spec.

The beforeEach() method

Since we need to test the Controller, Why not just create an instance of the controller instead of creating an instance of the component and extracting the controller from it? This is because the controller relies on it’s component, viewModel and other dependencies. Instead of manually setting up all of these dependencies, creating an instance of a component sets up all of these dependencies for us and the necessary wiring between them. So the component that is extracted this way is ready for testing.

beforeEach(function () {
    Ext.isDomReady = true
    this.record = Ext.create('SuperHeroes.model.Hero', {
        id: 1, first_name: "John", last_name: "Doe"
    })
    this.cmp = Ext.create('SuperHeroes.view.HeroGrid')
    this.ctrl = this.cmp.getController()

     this.formValues = {
        "first_name": "Tony",
        "last_name": "Stark"
    }

})

Now let’s look at one of the test cases for the onAdd() method.

  • Pretty much every test can be broken down to the following parts
    • Setup of test data and the necessary resources
    • Mock/Spy the components that the test case touches that are outside the scope of the “Unit of code” that is being tested
    • Invoke the “Unit Of code” that is to be tested (A method in this case)
    • Check if the behavior is as expected
  • In this case, we need to inspect if the form values are being inserted in to the store after validation and if the store is synced with the backend service. So we spy on both of these methods and test if they are invoked appropriately.
it('onAdd should add valid form values to the store', function(){

    //Since we are going to test the feature to add a new Super Hero, let's setup the sample input data

    const form = this.cmp.lookupReference('hero_add_form')
    const store = this.cmp.getViewModel().getStore('heroStore')
    form.getForm().setValues(this.formValues)
    const model = Ext.create('SuperHeroes.model.Hero', this.formValues);

    spyOn(store,"add")
    spyOn(store,"sync")
    spyOn(this.ctrl, "generateModel").and.returnValue(model)


    this.ctrl.onAdd()

    expect(store.sync).toHaveBeenCalled()
    expect(store.add).toHaveBeenCalledWith(model)
})

Ensure that there are sufficient test cases for each unit of code and all the necessary behaviors have their own separate tests. All critical behaviors of each unit of code must be covered by these tests (As an Example: Every branch condition of an if/else statement must have it’s own test case if the branching imparts a  different  behavior to the unit of code being tested).

Running the tests

Run the tests using karma by invoking “karma start test/karma.conf.js” from the root of the project.

user@userbox ~/dev/Unit-Testing-Sencha-ExtJS $ karma start test/karma.conf.js
13 09 2017 11:23:59.998:WARN [watcher]: Pattern "/home/uhsarp/dev/Unit-Testing-Sencha-ExtJS/test/spec/lib/**/*.ts" does not match any file.
13 09 2017 11:24:00.415:INFO [compiler.karma-typescript]: Compiling project using Typescript 2.5.2
...
13 09 2017 11:24:01.428:INFO [compiler.karma-typescript]: Compiled 2 files in 969 ms.
13 09 2017 11:24:02.075:WARN [karma]: No captured browser, open http://localhost:9876/
13 09 2017 11:24:02.100:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
13 09 2017 11:24:02.101:INFO [launcher]: Launching browser Chrome with unlimited concurrency
13 09 2017 11:24:02.364:INFO [launcher]: Starting browser Chrome
13 09 2017 11:24:09.504:INFO [Chrome 60.0.3112 (Linux 0.0.0)]: Connected on socket XYc5FNcTh984RlZ_AAAA with id 71514996
Chrome 60.0.3112 (Linux 0.0.0): Executed 8 of 9 SUCCESS (0 secs / 0.104 secs)
13 09 2017 11:24:12.658:WARN [web-server]: 404: /get?_dc=1505316252655&page=1&start=0&limit=25
WARN: '[W] [Ext.define] Duplicate class name 'SuperHeroes.view.HeroGridController' specified, must be a non-empty string'
WARN: '[W] [Ext.define] Duplicate class name 'SuperHeroes.view.HeroGridModel' specified, must be a non-empty string'
Chrome 60.0.3112 (Linux 0.0.0): Executed 9 of 9 SUCCESS (0.129 secs / 0.129 secs)

Source Code
https://github.com/batchu/Unit-Testing-Sencha-ExtJS.git