The TAF Kotlin Samples project is a test automation framework built using Kotlin and Gradle. The framework is designed to facilitate both core functionality testing and UI testing using Selenide. Below is an overview of the key components and configurations used in the project.
- Project Structure
- Key Features and Dependencies
- Programming Patterns Used in Test Automation Framework
- Working with framework
- TAF Core key features overview
- Author and Contact Information
The project consists of the following modules:
:core
: Contains core testing logic and utilities.:ui_selenide
: Focuses on UI testing, leveraging the Selenide library.
- Core & UI Testing: The framework supports both core logic and UI testing with Selenide.
- Parallel & Cached Execution: Leveraging Gradle features to improve build performance.
- Kotlin & Java 17: Built with Kotlin 2.0.20 and targeted for JVM 17.
The project uses modern libraries and tools to ensure high-quality testing:
- Kotlin 2.0+: For developing the framework.
- JUnit Jupiter 5+: For unit testing.
- Selenide 7.4+: For UI testing.
- Jackson 2.17+: For JSON processing and configuration management.
- Log4j 1.2.17 & SLF4J 1.7.32: For logging.
In the test automation framework, we utilize the Singleton pattern to ensure a single, globally accessible instance of certain classes. The Singleton pattern is a design pattern that restricts the instantiation of a class to one single instance and provides a global point of access to that instance.
- Single Instance: Guarantees that only one instance of the class exists throughout the application.
- Global Access: Provides a global point of access to the instance, allowing it to be accessed from anywhere within the application.
- Controlled Access: Ensures controlled access to the unique instance, preventing multiple instantiations.
In our framework, the Singleton pattern is implemented in
the CustomLogger
object.
The taf-kotlin-samples
project uses several system properties to configure various aspects of the Test Automation
Framework (TAF). These properties can be set at runtime to control the behavior of the WebDriver and the environment in
which the tests are executed.
Property Name | Description |
---|---|
webdriver.type |
Specifies the type of WebDriver to use (e.g., Chrome, Firefox). |
webdriver.host |
The host address where the WebDriver is running. |
webdriver.port |
The port number for the WebDriver. |
webdriver.browser.name |
The name of the browser to be used for testing (e.g., Chrome, Firefox). |
webdriver.browser.headless |
If set to true , runs the browser in headless mode. |
webdriver.nodeId |
The node ID for a Selenium Grid setup. |
webdriver.chrome.version |
Specifies the version of Chrome WebDriver to use. |
webdriver.firefox.version |
Specifies the version of Firefox WebDriver to use. |
environment |
Defines the target environment for the tests (e.g., development, staging, production). |
user.dir |
The working directory of the user executing the tests. |
DYNAMIC_PARAM |
A placeholder for dynamic parameters that can be set at runtime. |
System properties default values related to webdriver config could be changed
in default_driver_config.yaml
.
- Options: { REMOTE | LOCAL }
- Default value: { LOCAL }.
- Example:
-Dwebdriver.type=REMOTE
- Description: Selects the location where the WebDriver will be executed.
- remote: Starts a remote WebDriver session from a Selenium server with the configured
host
andport
. - local: Starts a WebDriver session on the local machine directly using the WebDriver binary.
- remote: Starts a remote WebDriver session from a Selenium server with the configured
- Options: Any valid host address.
- Default value: { 127.0.0.1 }.
- Example:
-Dwebdriver.host=192.168.1.100
- Description: Specifies the host address where the remote WebDriver server is running. This is required
when
webdriver.type
is set toremote
.
- Options: Any valid port number.
- Default value: { 4444 }.
- Example:
-Dwebdriver.port=4444
- Description: Specifies the port number for the remote WebDriver server. This is required when
webdriver.type
is set toremote
.
- Options: { CHROME | FIREFOX }
- Default value: { CHROME }
- Example:
-Dwebdriver.browser.name=FIREFOX
- Description: Specifies the browser to be used for testing.
- Options: { true | false }
- Default value: { false }
- Example:
-Dwebdriver.browser.headless=true
- Description: Determines whether the browser should run in headless mode, which means without a graphical user interface.
- Options: Any valid node ID.
- Default value: { tafSeleniumNode}.
- Example:
-Dwebdriver.nodeId=node01
- Description: Specifies the node ID for a Selenium Grid setup, used when running tests on a distributed setup.
- Options: Any valid Chrome WebDriver version.
- Default value: Latest available version.
- Example:
-Dwebdriver.chrome.version=127.0.6533.119
- Description: Specifies the version of the Chrome WebDriver to use.
- Options: Any valid Firefox WebDriver version.
- Default value: Latest available version.
- Example:
-Dwebdriver.firefox.version=0.26.0
- Description: Specifies the version of the Firefox WebDriver to use.
- Options: Any environment name.
- Default value: { development }
- Example:
-Denvironment=staging
- Description: Defines the target environment for the tests (e.g.,
development
,staging
,production
).
- Options: Any valid directory path.
- Default value: Current working directory.
- Example:
-Duser.dir=/path/to/project
- Description: Specifies the working directory of the user executing the tests. This is typically set automatically.
- Options: Any string value.
- Default value: None.
- Example:
-DDYNAMIC_PARAM=myValue
- Description: A placeholder for dynamic parameters that can be set at runtime. This allows for flexible and custom configuration during test execution.
You can set these system properties when executing your tests using the -D
flag in your command-line interface. For
example:
./gradlew test -Dwebdriver.type=chrome -Dwebdriver.browser.headless=true
The FileConvert
object simplifies converting YAML files
into Kotlin objects using Jackson's ObjectMapper
with Kotlin-specific configurations.
- File to Generic Object Conversion: Converts YAML files into objects of any specified type.
- Reflection Cache Size: Optimizes reflection-based access with a configurable cache size.
- Null Handling: Configurable null handling for collections, maps, and default values.
- Singleton Support: Optionally enables singleton support for Kotlin objects during deserialization.
- Strict Null Checks: Optionally enforces strict null checks.
// Convert a YAML file into a Kotlin object
val config: MyConfigClass = FileConvert.resourceFileToObject("config.yaml")
This framework provides a flexible way to work with system properties, which can be easily defined, accessed, and managed using the SystemProperties interface and TafSystemProperties enum. These properties allow the configuration of the WebDriver, Environment, and other critical settings without modifying the codebase directly.
The SystemProperties
interface provides a set of methods for managing system properties:
getName()
: Retrieves the name of the system property.get()
: Returns the value of the system property, ornull
if it is not set.getOrDefault(defaultValue: String)
: Returns the value of the system property or a default value if the property is not set.set(value: String)
: Sets the system property to the specified value.clear()
: Clears the system property and returns its previous value, if any.
// Get system property value
val webdriverType = WEBDRIVER_TYPE.get()
// Get system property value with a default fallback
val webdriverTypeOrDefault = WEBDRIVER_TYPE.getOrDefault("LOCAL")
// Set a system property
WEBDRIVER_TYPE.set("REMOTE")
// Clear a system property
WEBDRIVER_TYPE.clear()
This feature allows you to maintain a single configuration file that adapts to different environments by substituting dynamic parameters at runtime, enhancing the flexibility and reusability of your test automation framework.
The DynamicConfigField
interface is designed to
process and replace placeholders in configuration values with actual values provided at runtime. This is particularly
helpful when you have configuration parameters that need to be adjusted based on the environment in which the tests are
running.
-
Companion Object Constants:
DEFAULT_EMPTY_STRING_VALUE
: A constant used as the default value for dynamic parameters that are not set.PATTERN_TO_REPLACE
: The pattern used in configuration files that will be replaced with actual values.
-
Properties:
valueToUpdate
: Retrieves the dynamic value to replace the placeholder, fetched from system properties.
-
Functions:
processDynamicValue(value: String?)
: Processes the input value by replacing thePATTERN_TO_REPLACE
with the actual value obtained fromvalueToUpdate
.getDynamicFieldOrThrowConfigException(fieldName: String)
: Extension function forString?
that returns the processed dynamic value or throws an exception if the value is null or not properly initialized.
In a typical configuration file, you might have placeholders like DYNAMIC_PARAM
that need to be replaced based on
system properties. For example, if your configuration file includes a placeholder DYNAMIC_PARAM
for the host, it would
initially look like this:
host: DYNAMIC_PARAM
When your configuration class implements DynamicConfigField, you define the property as follows:
class Configuration : DynamicConfigField {
var host: String? = null
get() = field.getDynamicFieldOrThrowConfigException("host")
}
If the system property DYNAMIC_PARAM is set during runtime, for instance:
./gradlew test -DYNAMIC_PARAM=example.com
Accessing the host property from your configuration object will automatically replace the placeholder with the system property value, resulting in:
val configuration: Configuration = Configuration()
configuration.host // Returns "example.com"
The Custom Logger Utility provides logging functionality for tests, built on top of Log4j. It allows perform context-specific logging for each test case, outputting logs to uniquely named files, neatly organized by test name and date.
- Context-Aware Logging: Dynamically generates log file names based on test method and name.
- Custom File Appender: Configures Log4j to output logs to specific files using a custom log pattern.
- Thread-local Logging: Manages logger instances per thread to ensure thread safety.
1. LogInstance
Interface
Defines the core logging operations:
getLogger()
: Retrieves the current logger instance.setLoggerContext(testMethodName: String, testName: String)
: Sets up the logger context with custom file output based on the test method and name.resetLog()
: Resets the logger instance.
A utility function that configures a Log4j FileAppender with a custom log output pattern.
3. CustomLogger
Implements the LogInstance
interface, managing logger instances for each test case. The log file path is dynamically
generated based on the test name and method.
// Set up logging context for a specific test
CustomLogger.setLoggerContext("testMethod1", "TestName1")
// Log messages using the context-specific logger
val logger = CustomLogger.getLogger()
logger.info("This is a log message for TestName1")
// Reset the logger after the test
CustomLogger.resetLog()
The Execution Environment Provider allows you to dynamically manage and retrieve the current execution environment
for tests based on system properties. It is based on two main classes
ExecutionEnvironmentProvider
and
ExecutionEnvironmentType
- Dynamic Environment Selection: Retrieves the execution environment from system properties, allowing tests to be run in different environments like DEFAULT or DYNAMIC_ENV.
- Default Fallback: If no system property is set, it defaults to the DEFAULT execution environment.
// Retrieve the current execution environment
val environment = ExecutionEnvironmentProvider.getExecutionEnvironment()
// Handle different environments
when (environment) {
ExecutionEnvironmentType.DEFAULT -> println("Running in default environment")
ExecutionEnvironmentType.DYNAMIC_ENV -> println("Running in dynamic environment")
}
The UrlService
object provides methods for constructing HTTP and
HTTPS URLs with predefined prefixes.
- HTTPS/HTTP URL Generation: Easily create URLs with
http://
orhttps://
prefixes.
val secureUrl = UrlService.getHttpsUrl("example.com")
val nonSecureUrl = UrlService.getHttpUrl("example.com")
Author: Evgeny Tumashchick
For any questions, issues, or contributions, you can reach out to me via:
- Email: tumashchick@gmail.com
- LinkedIn: Evgeny Tumashchick
- GitHub: evgeny-tumashchick
Feel free to open an issue on the GitHub repository for bug reports, feature requests, or any other inquiries.