Get started

Annotations

Introduction

CSTesting provides TestNG-style annotations and a runner so you can structure tests with suite/class/method lifecycle. You mark test methods with @CSTest and use @BeforeSuite, @AfterSuite, @BeforeClass, @AfterClass, @BeforeMethod, and @AfterMethod for setup and teardown. Extend CSTestingTestBase to get a browser field that the runner injects before each test method. The runner creates a new browser for each test, runs the lifecycle methods in order, and closes the browser after @AfterMethod.

List of annotations

AnnotationWhen it runs
@BeforeSuiteOnce before the entire run (before any @BeforeClass or test). No browser is created yet.
@AfterSuiteOnce after the entire run (after all tests and @AfterClass). Browser is already closed.
@BeforeClassOnce before any test method in the class. No browser yet.
@AfterClassOnce after all test methods in the class. Browser already closed.
@BeforeMethodBefore each @CSTest method. Browser is created and injected before this runs.
@AfterMethodAfter each @CSTest method. Browser is still open; the runner closes it after this.
@CSTestThe test method. Runs with browser set.

All lifecycle and test methods must be public, no-argument instance methods. Multiple methods can share the same annotation (e.g. several @BeforeMethod methods); they are all invoked in declaration order.

Execution order

The runner runs in this order:

  1. @BeforeSuite (all methods)
  2. @BeforeClass (all methods)
  3. For each @CSTest method:
    • Create a new browser and inject it into the test instance
    • @BeforeMethod (all methods)
    • @CSTest (the test method)
    • @AfterMethod (all methods)
    • Close the browser
  4. @AfterClass (all methods)
  5. @AfterSuite (all methods)

So each test method gets a fresh browser; nothing is shared between tests except the same test class instance for suite/class hooks.

Base class: CSTestingTestBase

Your test class should extend CSTestingTestBase. The base class provides:

MemberDescription
protected CSTestingBrowser browserSet by the runner before each test. Use it in @BeforeMethod, @CSTest, and @AfterMethod.
protected CSTestingOptions getBrowserOptions()Override to customize browser options (e.g. headless vs headed). Default: headless.
public void setBrowser(CSTestingBrowser browser)Called by the runner to inject the browser. You normally don't call this yourself.

Example – run with a visible browser:

@Override
protected CSTestingOptions getBrowserOptions() {
    return CSTestingOptions.builder().headless(false).build();
}

@CSTest

Mark a method as a test with @CSTest. You can optionally set a description (e.g. for reports or logs):

@CSTest
public void testLogin() {
    browser.gotoUrl("https://example.com/login");
    browser.locator("name=user").type("alice");
    browser.locator("button[type=submit]").click();
    browser.assertThat().hasURL("**/welcome");
}

@CSTest(description = "Check example.com title")
public void testTitle() {
    browser.gotoUrl("https://example.com");
    browser.assertThat().hasTitle("Example");
}

Runner: CSTestingRunner

CSTestingRunner discovers and runs all @CSTest methods in a class and runs the lifecycle annotations.

From code:

// Default options (headless)
CSTestingRunner.run(MyTestClass.class);

// Custom options (e.g. headed)
CSTestingRunner.run(MyTestClass.class, CSTestingOptions.builder().headless(false).build());

From command line (no main() in the test class):

mvn exec:java -Dexec.mainClass="com.cstesting.runner.CSTestingRunner" -Dexec.args="com.example.MyTest"

Or with the annotation-tests profile (if configured to run a specific class):

mvn exec:java -Pannotation-tests

Entry point with class name as argument:

java -cp ... com.cstesting.runner.CSTestingRunner com.example.MyTest

The first argument must be the fully qualified class name of the test class.

Full example

import com.cstesting.annotations.*;
import com.cstesting.runner.CSTestingRunner;
import com.cstesting.runner.CSTestingTestBase;

public class MyTest extends CSTestingTestBase {

    @BeforeSuite
    public void beforeSuite() {
        System.out.println("Before suite");
    }

    @AfterSuite
    public void afterSuite() {
        System.out.println("After suite");
    }

    @BeforeClass
    public void beforeClass() {
        System.out.println("Before class");
    }

    @AfterClass
    public void afterClass() {
        System.out.println("After class");
    }

    @BeforeMethod
    public void setUp() {
        // browser is already created and set by the runner
    }

    @AfterMethod
    public void tearDown() {
        // runs before the runner closes the browser
    }

    @CSTest(description = "Check example.com title")
    public void testTitle() {
        browser.gotoUrl("https://example.com");
        browser.assertThat().hasTitle("Example");
    }

    @CSTest
    public void testHeading() {
        browser.gotoUrl("https://example.com");
        browser.assertThat(browser.locator("h1")).isVisible();
    }

    public static void main(String[] args) {
        CSTestingRunner.run(MyTest.class);
    }
}

Run:

  • From IDE: run main, or run the class with JUnit/TestNG if you integrate the runner there.
  • From Maven: mvn exec:java -Dexec.mainClass="com.cstesting.runner.CSTestingRunner" -Dexec.args="com.example.MyTest"

Running without extending CSTestingTestBase

The runner can inject the browser into a class that does not extend CSTestingTestBase if the class has either:

  • A setBrowser(CSTestingBrowser browser) method, or
  • A field named browser (any visibility) of type CSTestingBrowser.

The runner will use reflection to set the browser before each test. Prefer extending CSTestingTestBase for a clear contract and getBrowserOptions() support.

Summary

TopicCSTesting
Test method@CSTest or @CSTest(description = "...")
Suite@BeforeSuite, @AfterSuite — once per run
Class@BeforeClass, @AfterClass — once per class
Method@BeforeMethod, @AfterMethod — per test; browser is set before @BeforeMethod
Base classExtend CSTestingTestBase for browser and getBrowserOptions()
Run from codeCSTestingRunner.run(MyTest.class) or run(MyTest.class, options)
Run from CLICSTestingRunner with fully qualified class name as first argument
BrowserOne new browser per test; closed after @AfterMethod