Chapter 11. Application Testing

Our role as architects isn't just to tick the boxes, send the application to the client, and forget about it. We have a responsibility, both from a professional perspective and a business perspective to produce software that tries to exceed expectations. A part of this was mentioned in Chapter 10, Debugging and Performance, in which we discussed the need to build an application that responded quickly to user actions. Now, we'll talk about building a robust application, one that stands up to scrutiny when it's under intense use.

What does it mean for an application to be robust? It means that if we click on a button, we see the expected result. If we try and enter an invalid e-mail address, we see a validation message. If we refresh the page, we find ourselves on the same screen as before. If the network connection drops out, remote requests get retried later. If we try and break the application, and so on, can we succeed?

The core of building a robust application is that the application should always behave as the user expects, even in unexpected circumstances. We must recognize that developers (and architects) are fallible and are unlikely to be able to account for every possible ramification of even a minor code change; this is where bugs arise and why the struggle for robustness is constant and ongoing.

We need a safety net for the fallible nature of coding. If we rename a particular method in the product list view, can we guarantee that it won't affect the shopping cart? We can use find and replace in our text editor, but we can never be 100 percent certain without refreshing the application and working through the functionality of the product list and the shopping cart to show that the customer requirements are still fulfilled.

A quality assurance process is a safety net of which automated testing is a key component. When working with Ext JS, there are a multitude of tools that we can use and a range of approaches to ensure that our applications are built in a way that is conducive to automated testing. To this end, in this chapter, we'll look at:

  • The different types of test and when to use them
  • Keeping Ext JS code concerns separate to promote unit testing
  • Naming and coding conventions to assist with integration testing
  • Testing tools for unit tests and integration tests
  • Ext JS-specific testing tools

The goal in this chapter is to build an understanding of the advantages of testing, how to write Ext JS applications that are easy to test, and how to select and employ suitable testing tools. When we're done, we'll have covered all of the subjects an Ext JS architect needs to produce exemplary products.

Total testing

In this chapter, we'll cover two types of test, one at the detail level and one at the "big picture" level. The first, unit testing, is great for helping with the algorithms and calculations that often make up business logic; the second, integration testing, helps us make sure that customer requirements are met and the user experience is sound. Let's look at each in turn.

Unit testing

With unit testing, we unsurprisingly test a unit, a unit being an individual unit of code. This will generally be a whole class, but will focus on a single method depending on the circumstances. In a unit test, we'd be able to say something like this:

Create cart object
Add product #1 to cart
Add product #1 to cart
Assert that there is only one item in the cart

To set up the test, we add the same product to the cart twice. This should result in one line item with a quantity of two rather than two line items, each with a quantity of one. In the first test, we make the assertion that the cart count is equal to one, assuring us that adding to the cart won't add duplicates. The next test will check whether the quantity is incremented as expected:

Create cart object
Add product #1 to cart
Add product #1 to cart
Assert that first cart item has a quantity of two

It performs the same setup as the previous test, but then picks out the first line item in the cart and makes the assertion that its quantity is equal to two, one for each time the product was added to the cart.

Assert yourself

What's all this "assertion" business? It's simply a way of saying, "if this condition isn't met, something's wrong". In the previous examples, if the actual value and the expected value aren't equal, something's wrong. In most unit testing libraries, there are lots of different assertions, such as:

  • Equal
  • Less than
  • Greater than
  • Is it numeric?
  • Does it contain the specified value?

Each testing library has its own flavor of assert methods. Some will use slightly different terminology (such as expectations or specifications). The terminology is less important than the principles behind unit testing, which is to put an isolated piece of code under intense scrutiny.

Integration testing

While unit testing focuses on a small piece of functionality, integration testing goes to the opposite extreme. Its purpose is to check whether all of the moving parts of the application work together correctly, replicating some of the actions that a real-world user would take.

For this reason, integration testing will often be described as UI testing because it acts directly on the interface. Let's say that we want to verify whether the detail window will show when a product is clicked on. We can do something like this:

  1. Find the link for the product in question.
  2. Simulate a click event on the link.
  3. Verify that the DOM element with the product in question appeared as expected.

This is completely different to the kind of focus we had with unit testing in which we were drilling down to a single function or class in the code. Here, the actions we test will span multiple classes in the application, checking whether they're integrated and working together correctly.

Integration and differentiation

The nature of integration testing means that it operates on the same application that your users can see; the tests effectively load up a browser and simulate the path a user would take. However, rather than moving the mouse cursor manually in the same way as a user, integration test frameworks generally work by picking out HTML elements on the screen and allowing you to perform actions directly.

This is both good and bad. When a user negotiates a web page, they can generally spot the UI components they're interested in fairly quickly, but when the person writing the test comes to pick out this same component, they need a way of referencing it. The usual approach to this is to use CSS or XPath selectors. For example, to reference an element on the page with an ID with CSS and then XPath, use the following code:

#someElement
//*[@id="someElement"]

Also, another slightly more complicated code to get the first button in a container is as follows:

#container > button:nth-child(1)
//*[@id="container"]/button[1]

This goes some way in demonstrating a potential pain point of writing integration tests. What if the ID of someElement changes? It'll break the test, but it's a fairly simple fix. What if the ID of "container" changes? Well, not only will it break the previous example, but it'll also break any other tests that are looking for buttons or other elements within this div.

This is an ongoing problem with integration testing: the fragility of tests. Later in the chapter, we'll look at some methods to address this within Ext JS.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset