Bringing Test Automation to the next level by extending internal development tools - hero

Bringing Test Automation to the next level by extending internal development tools

It is well known that a side-effect of developing products is the accidental introduction of bugs. This is nothing new. There will always be bugs in the products we develop. Sometimes we discover them early but quite often they sneak their way out further down the release lanes.

At Celonis, testing is a crucial part in the way we develop software. Every single feature we develop is thoroughly tested in order to deliver products that meet customer value. In a world where IT systems evolve rapidly, where new features are deployed to production in a continuous fashion, it's of utmost importance that those features not only work as expected but are also performant, secure and compliant to regulations, and user friendly. Trying to balance these factors can be challenging, especially in systems that change and evolve rapidly over time.

Our QA teams use automated checks to deliver high-quality features and prevent releasing unstable software. A key factor in having an effective automation strategy in place is to have isolated, maintainable, concise and easy to read and write automated tests. After numerous evaluation of automation tools, we decided to use Cypress (Cypress.io). As our end-to-end test suites grew and evolved, so did our understanding with Cypress. At some point, we started thinking about extending Cypress functionalities by writing our own tools around it.

Responding to change

If you are familiar with Cypress, you probably know that Cypress provides built-in functionality to make HTTP requests (https://docs.Cypress.io/api/commands/request.html).

Let's have a look at a simple Cypress test that makes a login HTTP request:

    it('should open an analysis', () => {
    cy.request({		// 1. using cy.request() function to make a request
    method: 'POST',		// 2. defines the HTTP method
    url: '/api/public/auth',	// 3. which endpoint to call
    body: {			// 4. the request payload
        email: 'user@example.com',
        password: 'super-secret-password'
    }
})

cy.visit('/process-mining/ui')  // 5. Navigate to process analytics, bypassing the login
// further actions, steps and assertions ...
})
  

By using `cy.request()` (1.) we can make an HTTP request. It accepts a few arguments: the HTTP method (2.), the URL endpoint, (3.) an optional body object (4.) and more options. 

Once the request is sent and a proper 200 OK response is received, we are now authenticated and thus able to bypass the login UI. This enables us to directly navigate to Process Analytics (5.). 

This is a common approach if you want to bypass Login pages in order to reduce test execution time. On the other hand, the login tests reside elsewhere and test exactly that: the login functionality.

There is nothing wrong with writing Cypress requests in this way. However, if your API changes in the future, you will have to adopt that change in your tests as well. For example, the url now has versioning that looks like this: `/api/v2/public/auth`, or there’s a new feature "Two-factor authentication" requiring authentication to be passed as an additional body parameter etc. Thus, your Cypress requests get outdated very quickly. Now imagine having a cloud platform that has lots of microservices, each microservice having their own ever changing APIs. Automated tests can quickly fall into a maintenance nightmare.

If only there was a better way to respond to API changes?

Artery-api plugin to the rescue

Since most of our micro-services are written in Java and using Spring Boot, we decided to write a maven plugin called `artery-api` which would generate typescript files containing helper classes, types, functions and Cypress commands intended to simplify communication between the tests and services and make our test maintenance less painful. Initially, this plugin was designed to be used by our Angular front-ends but was later on extended for testing purposes.

Adding maven support to our Cypress project by including a pom.xml file that has dependencies to our microservices and a build phase that has a dependency to our artery plugin made it possible to dynamically generate helper classes during compile time.

ilir-xml-2

After compilation (mvn clean compile), looking at the Cypress project structure, we now see an auto-generated cloud-team-api.ts file containing the authentication logic and many other helper functions. What is really cool about it is the fact that whenever the authentication logic changes (depending on how big the change is) our tests will continue to run with minimal to almost zero maintenance. 

Awesome, right?

This is how the auto-generated file looks like:

    export class AuthenticationRequest {
    email: string;
    password: string;
}

export function Authentication_authenticate(authenticationRequest: AuthenticationRequest): Cypress.Chainable> {
    cy.log('Executing POST to /api/public/auth')
    return cy.request({
        'url': `/api/public/auth`,
        'method': "POST",
        'body': authenticationRequest,
    } as any)
}

Cypress.Commands.add('Team_Authentication_authenticate', Authentication_authenticate)
  

… and here is how it could be used in our test:

    it('should open an analysis', () => {
    const dummyUser = new AuthenticationRequest()
    dummyUser.email = 'user@example.com'
    dummyUser.password = 'super-secret-password'
    cy.Authentication_authenticate()

    cy.visit('/process-mining/ui')
    // further actions, steps and assertions ...

})
  

A few takeaways

At Celonis, we see testing activities and quality as a team effort. It's the best way to get things (testing) done right! The artery-api plugin was initially designed to help front-end development and never seen as a testing tool. But by working closely with our dev teams, we managed to extend artery-api and make it even more awesome. Furthermore, collaboration between QA and Dev teams not only makes testing better and more fun, but makes you a better tester along the way.

Happy testing!

Ilir Kosumi --author image
Ilir Kosumi
Senior QA Engineer

Ilir has been working as a Senior QA Engineer at Celonis since 2019. He started his career as a tester back in 2012. His attention to details and passion for quality has, since then, never stopped. He likes to learn and experiment with new technologies and how to apply them on his daily work, and enjoys sharing his knowledge with his fellow colleagues.
His hobbies are snowboarding on the beautiful mountains of Bavaria, cycling, reading books, and spending time with his family.

Dear visitor, you're using an outdated browser. Parts of this website will not work correctly. For a better experience, update or change your browser.