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
| Annotation | When it runs |
|---|---|
@BeforeSuite | Once before the entire run (before any @BeforeClass or test). No browser is created yet. |
@AfterSuite | Once after the entire run (after all tests and @AfterClass). Browser is already closed. |
@BeforeClass | Once before any test method in the class. No browser yet. |
@AfterClass | Once after all test methods in the class. Browser already closed. |
@BeforeMethod | Before each @CSTest method. Browser is created and injected before this runs. |
@AfterMethod | After each @CSTest method. Browser is still open; the runner closes it after this. |
@CSTest | The 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:
@BeforeSuite(all methods)@BeforeClass(all methods)- For each
@CSTestmethod:- Create a new browser and inject it into the test instance
@BeforeMethod(all methods)@CSTest(the test method)@AfterMethod(all methods)- Close the browser
@AfterClass(all methods)@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:
| Member | Description |
|---|---|
protected CSTestingBrowser browser | Set 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 typeCSTestingBrowser.
The runner will use reflection to set the browser before each test. Prefer extending CSTestingTestBase for a clear contract and getBrowserOptions() support.
Summary
| Topic | CSTesting |
|---|---|
| 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 class | Extend CSTestingTestBase for browser and getBrowserOptions() |
| Run from code | CSTestingRunner.run(MyTest.class) or run(MyTest.class, options) |
| Run from CLI | CSTestingRunner with fully qualified class name as first argument |
| Browser | One new browser per test; closed after @AfterMethod |