Writing Tests for Web Applications With TestProject

This blog post describes how we can write tests for web applications by using the TestProject framework. After we have finished this blog post, we:

  • Can write tests for web applications with TestProject framework.
  • Understand how we can use Selenium API in our test classes.
  • Know how we can pass input parameters to our test cases.

Let's start by writing our first test class.

This blog post is the third part of my TestProject tutorial that is sponsored by TestProject.io. However, the views and opinions expressed in this tutorial are mine.

This blog post assumes that:

By the way, you might want to read the other parts of my TestProject tutorial.

Writing Our First Test Class

When we write tests with TestProject framework, we have to follow these rules:

  • We must put our test classes to the src/main/java directory. We must put our test class to this directory because we have to package our tests to a jar file and publish our tests (upload the jar file) on the app.testproject.io website before we can run them.
  • We should describe the name and description of our test case (aka test class) by using the @Test annotation that is provided by the TestProject SDK. When we use this annotation, the app.testproject.io website uses the provided values in its test reports.
  • Because we are writing tests for a web application, our test classes must implement the WebTest interface provided by the TestProject SDK.
  • We can write a test case by implementing the execute() method of the WebTest interface. Our implementation must return a value of the ExecutionResult enum that specifies the outcome of our test case.

Next, we will write a test which ensures that my website has the correct title. We can write this test by following these steps:

First, we have to create a new test class that implements the WebTest interface. After we have created this class, its source code looks as follows:

import io.testproject.java.sdk.v2.enums.ExecutionResult;
import io.testproject.java.sdk.v2.exceptions.FailureException;
import io.testproject.java.sdk.v2.tests.WebTest;
import io.testproject.java.sdk.v2.tests.helpers.WebTestHelper;

public class WebSiteShouldDisplayExpectedTitleTest implements WebTest {

    @Override
    public ExecutionResult execute(WebTestHelper webTestHelper) 
            throws FailureException {
            
    }
}
The name of a test class must identify the tested feature and the expected outcome. This is important because it helps us to find the correct file when we want to read the source code of a specific test case.

Second, we have to configure the name and description of our test case. We can do this by annotating our test class with the @Test annotation.

After we have configured the name and description of our test case, the source code of our test class looks as follows:

import io.testproject.java.annotations.v2.Test;
import io.testproject.java.sdk.v2.enums.ExecutionResult;
import io.testproject.java.sdk.v2.exceptions.FailureException;
import io.testproject.java.sdk.v2.tests.WebTest;
import io.testproject.java.sdk.v2.tests.helpers.WebTestHelper;

@Test(
        name = "My website should display the expected title",
        description = "Verify that my website displays the correct title"
)
public class WebSiteShouldDisplayExpectedTitleTest implements WebTest {

    @Override
    public ExecutionResult execute(WebTestHelper webTestHelper) 
            throws FailureException {
            
    }
}
There are two things I want to point out:

First, the description of this test case is not very useful because I wanted to avoid horizontal scrolling. Remember that you should always use a description that doesn't just repeat the name of your test case.

Second, we should always specify the name and description of our test case by using the @Test annotation. Because the app.testproject.io website uses this information in its test reports, this approach helps us to locate the problem if a test case fails. If a test case fails, and it doesn't follow this rule, we have to read its source code before we know what the problem is. This is a waste of time.

Third, we have to implement the execute() method by following these steps:

  1. Get a reference to the WebDriver object which controls the web browser that runs our test.
  2. Ensure that the used web browser opens my website.
  3. Determine the return value of the execute() method. If my website has the correct title, we must return ExecutionResult.PASSED. On the other hand, if my website doesn't have the expected title, we must return ExecutionResult.FAILED.

After we have written our test case, the source code of our test class looks as follows:

import io.testproject.java.annotations.v2.Test;
import io.testproject.java.sdk.v2.enums.ExecutionResult;
import io.testproject.java.sdk.v2.drivers.WebDriver;
import io.testproject.java.sdk.v2.exceptions.FailureException;
import io.testproject.java.sdk.v2.tests.WebTest;
import io.testproject.java.sdk.v2.tests.helpers.WebTestHelper;

@Test(
        name = "My website should display the expected title",
        description = "Verify that my website displays the correct title"
)
public class WebSiteShouldDisplayExpectedTitleTest implements WebTest {

    @Override
    public ExecutionResult execute(WebTestHelper webTestHelper)
            throws FailureException {
        WebDriver browser = webTestHelper.getDriver();
        browser.get("https://www.petrikainulainen.net");
        return browser.getTitle()
                .equals("Petri Kainulainen — Developing Software With Passion")
                ? ExecutionResult.PASSED
                : ExecutionResult.FAILED;
    }
}
There are three things I want to point out:

First, we will use the WebDriver class provided by the TestProject SDK because it provides additional exception handling capabilities. However, if you don't want to use these features, you can also use the standard WebDriver interface provided by the Selenium API. Also, keep in mind that the WebDriver class provided by the TestProject SDK implements the "standard" Selenium API.

Second, because we are writing tests for web applications, our tests will use the Selenium API. This means that it's a good idea to follow Selenium best practices. Unfortunately, this is a quite broad topic and it's impossible to cover it in this blog post. However, we can get started by reading these articles:

Third, when we determine the return value of the execute() method, we should use one of the these two values of the ExecutionResult enum:

  • FAILED means that our test case failed.
  • PASSED means that our test case passed.

We can now write simple tests for web applications by using the TestProject framework. However, sometimes we want to pass input parameters to our test case. Next, we will find out how we can do it.

Passing Input Parameters to Our Test Case

When we want to pass an input parameter to our test case, we have to follow these steps:

  1. Add a private field to our test class and ensure that the field can store String objects.
  2. Annotate our new field with the @TestParameter annotation. When we do this, we can set the default value of our input parameter by setting the value of the @TestParameter annotation's defaultValue attribute.

Let's write a test case which ensure that a website has the expected title. We can write this test case by following these steps:

First, we have create a new test class and ensure that our test class implements the WebTest interface. After we have created this test class, its source code looks as follows:

import io.testproject.java.sdk.v2.enums.ExecutionResult;
import io.testproject.java.sdk.v2.exceptions.FailureException;
import io.testproject.java.sdk.v2.tests.WebTest;
import io.testproject.java.sdk.v2.tests.helpers.WebTestHelper;

public class ParameterizedWebSiteShouldDisplayExpectedTitleTest implements WebTest {

    @Override
    public ExecutionResult execute(WebTestHelper webTestHelper)
            throws FailureException {
        
    }
}

Second, we have to configure two input parameters:

  • The expectedTitle parameter contains the expected title of the opened website. The default value of this parameter is: 'Petri Kainulainen — Developing Software With Passion'.
  • The url parameter contains the url of the opened website. The default value of this parameter: 'https://www.petrikainulainen.net'.

After we have configured these input parameters, the source code of our test class looks as follows:

import io.testproject.java.annotations.v2.TestParameter;
import io.testproject.java.sdk.v2.enums.ExecutionResult;
import io.testproject.java.sdk.v2.exceptions.FailureException;
import io.testproject.java.sdk.v2.tests.WebTest;
import io.testproject.java.sdk.v2.tests.helpers.WebTestHelper;

public class ParameterizedWebSiteShouldDisplayExpectedTitleTest implements WebTest {

    @TestParameter(
            defaultValue = "Petri Kainulainen — Developing Software With Passion"
    )
    public String expectedTitle;

    @TestParameter(defaultValue = "https://www.petrikainulainen.net")
    public String url;

    @Override
    public ExecutionResult execute(WebTestHelper webTestHelper) 
            throws FailureException {
        
    }
}

Third, we have to configure the name and description of our test case. When our test case has input parameters, we can add the parameter values to the description of our test case by using the format: {parameterValue}.

After we have configured the name and description of our test case, the source code of our test class looks as follows:

import io.testproject.java.annotations.v2.Test;
import io.testproject.java.annotations.v2.TestParameter;
import io.testproject.java.sdk.v2.enums.ExecutionResult;
import io.testproject.java.sdk.v2.exceptions.FailureException;
import io.testproject.java.sdk.v2.tests.WebTest;
import io.testproject.java.sdk.v2.tests.helpers.WebTestHelper;

@Test(
        name = "The opened website should display the expected title",
        description = "The website: {url} should display the title: {expectedTitle}"
)
public class ParameterizedWebSiteShouldDisplayExpectedTitleTest implements WebTest {

    @TestParameter(
            defaultValue = "Petri Kainulainen — Developing Software With Passion"
    )
    public String expectedTitle;

    @TestParameter(defaultValue = "https://www.petrikainulainen.net")
    public String url;

    @Override
    public ExecutionResult execute(WebTestHelper webTestHelper) 
            throws FailureException {
        
    }
}

Fourth, we have to implement the execute() method of the WebTest interface and ensure that the opened website has the correct title. After we have written this method, the source code of our test class looks as follows:

import io.testproject.java.annotations.v2.Test;
import io.testproject.java.annotations.v2.TestParameter;
import io.testproject.java.sdk.v2.drivers.WebDriver;
import io.testproject.java.sdk.v2.enums.ExecutionResult;
import io.testproject.java.sdk.v2.exceptions.FailureException;
import io.testproject.java.sdk.v2.tests.WebTest;
import io.testproject.java.sdk.v2.tests.helpers.WebTestHelper;

@Test(
        name = "The opened website should display the expected title",
        description = "The website: {url} should display the title: {expectedTitle}"
)
public class ParameterizedWebSiteShouldDisplayExpectedTitleTest implements WebTest {

    @TestParameter(
            defaultValue = "Petri Kainulainen — Developing Software With Passion"
    )
    public String expectedTitle;

    @TestParameter(defaultValue = "https://www.petrikainulainen.net")
    public String url;

    @Override
    public ExecutionResult execute(WebTestHelper webTestHelper) 
            throws FailureException {
        WebDriver browser = webTestHelper.getDriver();
        browser.get(url);
        return browser.getTitle().equals(expectedTitle)
                ? ExecutionResult.PASSED
                : ExecutionResult.FAILED;
    }
}
If we want to provide a description to our input parameter, we can set the value of the @TestParameter annotation's description attribute. Note that the value of this attribute is visible on the app.testproject.io website. To be more specific, the description of our input parameter is shown on the view that allows us to override the default parameter values of the invoked test cases.

We can now configure the input parameters that must be provided before we can run our tests by using the TestProject agent. We will talk more about this when we learn to run our test cases.

Let's summarize what we learned from this blog post.

Summary

This blog post has taught us five things:

  • We must put our test classes to the src/main/java directory.
  • We should specify the name and description of our test case by using the @Test annotation.
  • If we write tests for a web application, a test class must implement the WebTest interface provided by the TestProject SDK.
  • We can get a reference to the WebDriver object by using the getDriver() method of the WebTestHelper class.
  • We can pass input parameters to a test case by using the @TestParameter annotation.

P.S. You can get the example application of this blog post from Github.

1 comment… add one

Leave a Reply