Write once and run multiple test cases with Cypress

Very early in my test automation career, I came across the legendary software engineer and author Robert C. Martin (aka Uncle Bob) who taught me that unnecessarily repeating code was a terrible idea. Not only because it leads to software systems that are hard to maintain and more prone to bugs and errors. But if large parts of the code base is compromised of code that is copy + pasted with a few variable names changed. Then if volatility is introduced via a change in the requirements or modification to implemented logic, then large parts of the solution will be made redundant with the click of someone else’s mouse.

So for combating this, instead of writing code to perform multiples of the same function with only a few slight changes to the data we are using. We should instead focus on only coding one test (in our case) and rely instead on the test automation framework to run our required test cases with the various scenarios that we ask of it. The benefits of this go beyond a solution that is easier to maintain and more flexible to outside changes. It also enables us to ramp up our testing efforts and run more test cases if needed.

For instance, you might only need to run 5 tests at the moment. But what if you need to run 15, or even 50 one day? With a code once and test multiple times approach. The ability for us to scale is much easier than if we took the approach of repeating every test case and making a few alterations.

This approach is not applicable in every scenario, and there may be situations in which we implement logic in one of our test cases and needs to be used in another. But this alone should not be a reason to take the simple approach and reuse code. It instead should only be done if there is no other solution available.

Defining our problem

Before we start to code, we should start with a basic understanding of what we are trying to achieve.

In essence, our goal is to have a single test case that is reusable and allows us to pass in our predefined test data and be able to run it with no modifications to the test code.

We will use the example of a fictional web application in which our tests are based on the prerequisite of logging into the application to perform our desired testing functions. In this example we simply want to assert that the page we are navigating to once we are logged in is displaying the correct page title. Instead of putting that login code in every it block and repeating ourselves. Let’s instead place it in with the provided beforeEach block so it is run automatically before the execution of each of our test cases.

describe(`Dynamic Data Test`, () => {
    beforeEach(() => {
        cy.visit("/login");
        cy.get('#username').type('admin')
        cy.get('#password').type('password')
        cy.contains("button", "Login").click()
    });
});

Personally, I would use the Custom Commands feature the Cypress provides to abstract away this logic and make my Cypress code more descriptive. You can read about Custom Commands here.

describe(`Dynamic Data Test`, () => {
    beforeEach(() => {
        cy.visit('/login')
        cy.login({ username: 'username', 
                   password: 'password' });
        });

     it("Makes sure the homepage is visible", function () {
            cy.contains('Welcome to the homepage!').should("be.visible")
     });
});

That’s better.

This test layout is fine if we want to use a fixture to supply our test data, but for our needs, I find it is better to write a require statement to load and store our data:

const testData = require("../fixtures/test_data/data.json")

describe(`Dynamic Data Test`, () => {
    beforeEach(() => {
        cy.visit('/login')
        cy.login({ username: 'username',
                   password: 'password' });
        });

    it("Makes sure the homepage is visible", function () {
        cy.visit('/home')
        cy.contains('Welcome to the homepage!').should("be.visible")
    });
});

We’re making progress! But our test is still static at the moment. We still have the problem of having to write multiple tests to check the title of various pages. So let’s add some more code to make it run our dynamic test cases.

Using JavaScript’s built-in looping

The first thing to realise is that out of the box, JavaScript is already fantastic at running the same bits of code repeatedly. We only need to tell it what we want it to do and if will repeat the designated code for us. But what do we want to repeat?

In the code above, we are loading a JSON file and assigning it to the variable testData. So let’s start by making sure that we have the contents of the file set up how we want it so that JavaScript can work with it.

[
    {
        "Title": "Homepage",
        "location": "/home"
    },
    {
        "Title": "Service Page",
        "location": "/services"
    },
    {
        "Title": "Contact Page",
        "location": "/contact"
    },
]

Here we are basically using an array of JSON objects with the first pair of key and values representing the title we expect, and the second being the location of where we are expecting to see it. Of course, this is a very simplistic demo. But you can extend it to be more complex.

The final step is to put it all together:

const testData = require("../fixtures/test_data/data.json")

testData.forEach((testCase) => {
    describe(Dynamic Data Test, () => {
     beforeEach(() => {
        cy.visit('/login')
         cy.login({ username: 'username',
                    password: 'password' });
        });

    it(Makes sure the ${testCase.title}} is visible, function () {
            cy.visit(${testCase.Location})
            cy.contains(`Welcome to the ${testCase.Title}`).should("be.visible")
        });
    });
});

This code will use the data stored within the JSON file to drive our defined test case without the need to repeat ourselves with multiple spec files. Testing within different scenarios is something that every test automation solution needs to do, but we needn’t do it at the detriment to the maintainability and future scalability of our framework. So next time before you write a bit of code to test a piece of functionality, ask yourself. Is there already a test that you could extend or use here?

I hope you found this blog post useful, Feel free to connect with me on Linkedin, Twitter, or via my contact form if you have a test automation question. Or would just like to say hello.

JavaScript for Cypress testing

I have always had a preference for starting things in the beginning and then continuing linearly. It doesn’t feel right to be learning about the consequences of an event before knowing what the causation was in the first place. I don’t, for instance, see how you can fully appreciate the advantages of people having alarm clocks in the home if you didn’t know that a private employee with a stick was once relied on to get you out of bed.

That fact alone makes me utterly grateful to own an alarm clock. But I digress.

As software testers, we know that the best time to start testing is as early as possible in the lifecycle of a project. And I think the same is true of knowledge building (just not the being involved part – unless you have a time machine). If you have knowledge on the building blocks that a larger project is built on top of. Then you are in a better position to become more knowledgable, efficient and productive with the particular skill and knowledge area that you are working in.

The test automation framework Cypress is easy enough to pick up on its own. I think if someone took some time to read their documentation and tinkered around with some of the provided examples. They could probably come away with some code that would meet their needs. That’s all a tool of any kind has to achieve afterall.

“The secret to getting ahead is getting started.”

However, if you want Cypress to exceed your expectations and really work for you in a way that makes your life as an automation engineer as easy as possible. Then you need to take some time to brush up on some JavaScript fundamentals. But since JavaScript is a monolithic beast with use cases ranging from creating and powering web servers, creating rich and engaging animations, or even powering entire operating systems in the browser. It can be hard to know where to start or even know what is relevant to you.

One thing that I’ve discovered with Cypress is that you don’t need to know a lot to do a lot. There is really only a handful of core operations that you really should be aware of before diving into the framework. Partially because you won’t think that Cypress is magic and doing multiple things for you out of the box (it certainly does help you in certain areas). But like I’ve said above. Understanding what it’s doing and how it’s doing it. Will not only make you more knowledge but also a far more effective and efficient user of the framework.

This, of course, isn’t an entire overview of every JavaScript function that is used with Cypress. I strongly encourage you do your own research into what you want to achieve and work back from there to gain your own understanding.

In stead think of this as an introduction to some core features of the language that you’ll need to be familiar with to work with Cypress on a daily basis.

Closures and Promises

Closures and promises are one of the most common JavaScript features that you’ll be using within your tests (whether you know it or not). A promise allows us to run callback functions on the code that is returned on successful completion.

promise.then(function(result) {
console.log(result); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});

In Cypress, the most common form of a promise is the .should method which takes in an assertion you would like to make and the value that you would like to assert on.

For example:

cy.get(.item).should("be.visible)

Assertions made via .should also have built-in retry-ability meaning that any assertions made through the command will be automatically called again until there is a time out, or it is successful.

var vs let vs const

Typically, you don’t need to use assignments in Cypress (you should use an alias instead). But there certainly are some use cases which I feel doesn’t totally invalidate this explanation.

For example, you may want to issue a Promise using the .then command which allows you to assert the contents of a div without making multiple calls to the DOM.

cy.get('.div').should(($div) => {
    const $body = $div.contents().find("body");
    expect($body).to.contain("Hello World!");
})

Here I am using a const to keep track of the body and then running assertions on its contents. Why am I using const here? Essentially, const values (short for constant, can’t be changed and will throw an error if you try to reassign them.

let/var are fairly interchangeable outside of functions (read up on an in-depth comparison here as their scope does vary).

Object deconstruction

This is probably most useful if you are passing multiple objects between your test cases and need a way to access the contents of the object without passing every key in one at a time.

So given something this this.

const user = {
    "email": "something@example.io",
    "password": "12345"
  };
  const email = user.email;
  const password = user.password;

We don’t have to make our test cases look like this.

cy.login({
    userEmail: user.email,
    userPassword: user.password,
});

Arguably, this is already clean code, it’s declarative and easy to follow and understand what it is doing. But it does get fairly repetitive after a while. Plus, what if you didn’t want to pass in one line at a time and instead only wanted to pass in the user object? After all, it contains all the key values that you want to reference.

This is where deconstruction can help and essentially takes one large object and instead of making multiple line assignments. You only need to write a single line.

const { email, password} = user;

What this code is saying is: ‘Give me a variable called email and password and take it from the user object”.

cy.login({ user });

Doesn’t that look nicer?

Like I say, JavaScript is a huge language and to do a complete overview couldn’t fit into one blog post.

If you are interested though. I would recommend taking a look at the ForEach loop that JavaScript offers. Combining these with fixtures is an excellent way to generate dynamic test cases from predefined data in JSON format.

This was a brief overview of the common JavaScript features that you’ll be using in Cypress. But if you have a JavaScript or test automation question. Feel free to contact me on Twitter, Linkedin or via my contact form.

TestProject is now better than ever

When I meet someone who I haven’t met before, and we get talking about what each of us does for a living. After getting past the explanation of what software testers do (hint: It’s not breaking software). I like to drop in some details about my work with automation and how I work with tools to enable applications to perform predefined actions.

Usually, this gets reaction that you are probably thinking, and they say something like: ‘Wow, I wish I could do that!’

But the thing is, they can if they really wanted to. As much as automation engineers like to think we have unique abilities and can make the DOM submit to our every need. Test automation isn’t complex in the slightest.

Back when I first started, I had no other option than to start learning a programming language (which first started off as being Perl, then Python and finally C# and Java). As well being familiar with a framework like Selenium or Robot Framework. Nowadays however with advancements in technology and the availability of newer and better tools. The barrier to entry has come way down and now anyone with a passion to apply test automation to a new or existing project. Only needs to do a bit of learning and they can jump right in and start reaping the rewards almost immediately.

Read More

Why your tests are failing and how to combat unreliable automation

If you have previously created automated test scripts, or are in the early stages of creating a new solution. You have likely come across situations where things, for lack of a better word. Brake, and seemingly for no easily explainable reason.

Either they have previously passed and are now failing. Failing inconsistently. Or possibly never passing, but getting closer to the end with every attempted run that you make.

And despite the temptation to immediately rerun any test that may have failed and chalk up anything that doesn’t meet your expected results to ‘one of those things’. You really need to take the time to understand what caused the failure to occur. Put in the steps in place to prevent it from happening in the future, and you” come away with a more reliable and effective test automation suite.

Read More

Using Faker to generate data for your Cypress tests

I’m a man of simple pleasures. Good books to keep the mind active and exposed to fresh ideas. Music that I can enjoy and relax to when needed. And also doing things with as little extra effort then required. Or sometimes referred to as taking the path of the least resistance. Why push a boulder up a hill if you don’t have to?

One of my favourite quotes by Bill Gates touches on this very topic and I think it illustrates perfectly the mindset that I try to apply to my daily life.“I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.”

“I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.”

Bill Gates

Lazy seems to have a negative association in today’s society and coupled often with the image of someone laying in a hammock and snoozing the day away while avoiding more important work.

The word originates from the 1540s word lasey whose original definition was someone that was “verse to labor, action, or effort“. And despite the modern-day connection with the word idle and the associated images that it brings to mind. I’m happy to associate myself with at least part of the original definition.

Because like I say. Why do something the hard way if you don’t need to?

Read More

Making use of custom commands and fixtures to reduce repeated code in your Cypress automation

The array of original web applications released today is outstanding. From blogging platforms, marketing tools and even websites dedicated to sharing photographs of your beloved pet cat. The diversity in the web’s utilisation for users to work, play and get tasks done is huge.

But there’s some functionality that nearly every application out there today has in common with one another. Whether its to keep track of how often you use particular services. Enable the ability to provide a rich and tailored experience. Or just restrict access to allow certain users. The process of registration and logging in is one of the most common tasks that users have to do when they want to interact with a new web application.

And for testers trying to construct an automation framework or toolset. These can be some of the most challenging parts of any automation approach. Not because it’s notoriosly difficult. But more because there are several unique approaches to achieving the desired goal:

  • You could use a static account. Already pre-configured and authenticated.
  • You could create a new account on the fly. Simulating the process of a real user.
  • You could also seed the database with an API POST request. Allowing you to mock login responses and other core functionality.

Each one of these options has their own pros/cons and I would highly suggest taking the time to think about which one not only works for your situation. But which one will provide the most value to the project that you are working on.

Read More

Consider the unconsidered

Our ability to predict human behaviour in various contexts is remarkably accurate. From measuring the likelihood of us buying a certain number of items based on our past shopping history. To whether we might want to go on holiday by analyzing our past Google search results (I’m sure you’ve seen the targeted ads).

And even though we can do amazing things with software and hardware to help us in predicting behaviour. We still run into issues in certain situations.

Think of a scenario in which an automatic car is driving down the road, and we throw an obstacle into its path. Will the car detect what is going on and take corrective action? Or will the car carry-on and cause a potential accident?

The answer is a favourite response of mine: ‘It depends’.

Read More

Automated API Testing with Cypress

Following on from my recent post detailing why you should be focusing on API testing (if you aren’t already). I wanted to show an example of a testing tool that I’m sure many people are interested in using for their automated UI testing needs and detail how to use it to perform automated API testing.

Why use Cypress?

If your tech stack is dependent on JavaScript in the front-end with a framework like React or Vue. And you’re making use of it in the back-end layer with a service like Firebase. Then using a JavaScript end-to-end testing tool might be something that you may want to consider.

New framework
Read More

Testing Can Be A Risky Business

It’s not uncommon for a software development project to take months or even years to be completed and reach the hands of customers. Or with Duke Nukem Forever.. decades.

And yet, with all that development time and anticipation by your end-users to get to use a piece of software. One incorrect feature or obscure bug can render the entire development process to worthless in less than a minute. This can not only be an infuriating experience for everyone involved. It is a perfect example of why an effective and robust testing strategy is not only crucial. But with software or hardware that is dealing with human life. It would be crazy not to have been included.

Read More

Strategic Test Automation

Testing is a critical part of any successful software development life-cycle. With demand ever-increasing for new digital solutions and quicker release cycles needed. The migration of previous manual test cases to an automated process is being put into action by more and more development teams as time goes on. And with amazing tools now available, such as Cypress and TestProject. Making the switch and reaping the rewards that test automation unleashes is simpler than ever.

With many benefits such as increases in efficiency, cost reductions less human interaction. Companies have a lot to gain from adopting robust test automation strategies and if used right. They will gain a valuable addition to their testing arsenal.

Unfortunately, many teams either start off on the wrong footing. Or when they get a new tool and use it. They have failed to have done the essential groundwork needed to know what they will do with it.

Read More