QA Interview Hub · Knowledge base · Quizzes

Java basics

The test project

Maven: pom.xml, scope, Surefire and Failsafe, running tests

Maven builds a project from the description in pom.xml: coordinates (groupId, artifactId, version), dependencies, plugins, properties. The standard layout: src/main/java, src/test/java, src/test/resources. In a tests-only project the code lives in src/test.

  • A dependency with <scope>test</scope> is available only when compiling and running tests and does not enter the artifact. Test libraries (JUnit, AssertJ, REST Assured) have this scope.
  • Versions go into <properties> or a BOM (dependencyManagement) so they do not drift. mvn dependency:tree shows where a conflicting version came from.
  • The lifecycle: validate → compile → test → package → verify; a command runs all phases up to the given one. That is why mvn verify also runs Failsafe tests.

Surefire runs unit tests in the test phase (by default classes *Test, Test*, *Tests, *TestCase). Failsafe is meant for integration tests (*IT, IT*, *ITCase); its goals are integration-test and verify. A failure does not stop the build at once, so the post-integration-test phase (shutting down the test environment) still runs, and verify checks the result. Run mvn verify, not mvn integration-test. Automation projects for UI/API often use only Surefire, and Failsafe when tests run after services are brought up.

Useful runs:

mvn test -Dtest=LoginTest                 # one class
mvn test -Dtest=LoginTest#validLogin      # one method
mvn test -Dgroups=smoke                   # JUnit 5/6 tags / TestNG groups
mvn test -DexcludedGroups=slow
mvn verify -Pstaging -Dbrowser=firefox    # a profile and a system property
mvn -DskipTests package                   # compiles tests but does not run them
mvn -Dmaven.test.skip=true package        # does not even compile tests

A profile (<profile>) changes dependencies, properties or plugin configuration for an environment. User properties from -D are passed by Surefire to the test process, so in code they are available through System.getProperty("browser", "chrome").

Gradle in brief, configuration through properties and env, SLF4J logging

Gradle is an alternative to Maven with a build.gradle (Groovy) or build.gradle.kts (Kotlin) script. The same things: dependencies, plugins, tasks. Differences for an automation engineer:

  • the script is code, so it is more flexible but harder to read; there is an incremental build and a cache;
  • JUnit 5 is enabled explicitly: tasks.test { useJUnitPlatform() }; tags — useJUnitPlatform { includeTags("smoke") };
  • a run: ./gradlew test --tests "*LoginTest"; gradlew (the wrapper) pins the tool version, like mvnw in Maven. Use the wrapper rather than a globally installed Gradle/Maven.

Test configuration. Do not hard-code the URL, browser and credentials. Layers usually look like this: defaults in a .properties file → overrides by system properties (-Dbase.url=...) → environment variables for secrets (System.getenv("API_TOKEN")). Maven Surefire passes a system property into tests, while in Gradle it must be forwarded explicitly in the test task. In CI, secrets come from a secret store, not from the repository, and are not printed to the log.

Logging through SLF4J. SLF4J is a facade: code writes to org.slf4j.Logger and the real work is done by an implementation (Logback, Log4j 2) on the classpath. There must be one implementation, otherwise you get a warning about multiple bindings.

private static final Logger log = LoggerFactory.getLogger(CheckoutTest.class);

log.info("Order {} created for user {}", orderId, userId);   // not "..." + orderId
log.error("Checkout failed for order {}", orderId, exception); // exception as the last argument
  • The {} placeholders format the message only when the level is enabled, unlike concatenation, which builds the string always.
  • Do not use System.out.println: no levels, no time, no file output.
  • Do not log passwords and tokens. The level (INFO in CI, DEBUG locally) is controlled in logback-test.xml.
  • With Lombok, @Slf4j is enough.