Create reusable test components
To create reusable test components with custom behavior for different phases of the test lifecycle, derive your class from UITestComponent. Override its lifecycle methods to implement your logic at the appropriate stages.
Lifecycle methods
UITestComponent provides four lifecycle methods you can override:
| Method | When it's called | Intended purpose |
|---|---|---|
Initialize(AbstractUITestFixture) |
When the component is added to the test fixture | Set up the component and initialize references |
BeforeTest() |
Before each test runs | Prepare test-specific state |
AfterTest() |
After each test completes | Clean up test-specific state |
Shutdown() |
When removing the component or tearing down the fixture | Release resources and perform final cleanup |
Example: Custom logging component
The following example creates a custom test component that logs messages during different stages of the test or test fixture:
public class TestLoggerComponent : UITestComponent
{
string testLoggerTag = "|| TestLoggerComponent || ";
string currentTest;
// Invoked when the test component is added to the test fixture.
protected override void Initialize(AbstractUITestFixture testFixture)
{
// Required: call the base Initialize.
base.Initialize(testFixture);
Debug.Log($"{testLoggerTag} Component Initialized");
}
// Invoked before each test.
protected override void BeforeTest()
{
currentTest = TestContext.CurrentContext.Test.Name;
Debug.Log($"{testLoggerTag} Beginning test: {currentTest}");
}
// Invoked after each test.
protected override void AfterTest()
{
Debug.Log($"{testLoggerTag} Ending test: {currentTest}");
}
// Invoked at the end of the test fixture.
protected override void Shutdown()
{
currentTest = null;
Debug.Log($"{testLoggerTag} Test Fixture Shutdown");
}
public void LogMessage(string message)
{
Debug.Log($"{currentTest}: {message}");
}
}
Add your custom test component to a test fixture using the AddTestComponent<T>() method:
public class TestLoggerComponentExample : UITestFixture
{
TestLoggerComponent testLoggerComponent;
[OneTimeSetUp]
public void OneTimeSetUp()
{
testLoggerComponent = AddTestComponent<TestLoggerComponent>();
}
[Test]
public void MyTestWithLoggingComponent()
{
// Test steps.
// ...
}
[Test]
public void MyOtherTestWithLoggingComponent()
{
testLoggerComponent.LogMessage("Adding more logs here");
// Test steps.
// ...
}
}
Example: UI setup component
The following example creates a custom test component that sets up the test fixture UI:
public class UISetupComponent : UITestComponent
{
public Button testButton { get; private set; }
protected override void Initialize(AbstractUITestFixture testFixture)
{
// Required: call the base Initialize.
base.Initialize(testFixture);
// Force fixtures that use this test component to clear
// the content of the rootVisualElement at the end of each test.
fixture.clearContentAfterTest = true;
}
protected override void BeforeTest()
{
// Instantiate your elements.
testButton = new Button() { name = "UISetupComponentButton" };
// Add them to the rootVisualElement.
fixture.rootVisualElement.Add(testButton);
}
protected override void AfterTest()
{
// Clean up of the rootVisualElement is automatic
// when clearContentAfterTest is true.
// You can add custom cleanup here if needed.
testButton = null;
}
}
To use it in your tests:
public class TestUISetupComponentExample : UITestFixture
{
UISetupComponent uiSetupComponent;
[OneTimeSetUp]
public void OneTimeSetUp()
{
uiSetupComponent = AddTestComponent<UISetupComponent>();
}
[Test]
public void MyTestWithUISetupComponent()
{
Button button = rootVisualElement.Q<Button>("UISetupComponentButton");
Assume.That(button, Is.Not.Null);
// Test steps.
// ...
}
[Test]
public void MyOtherTestWithUISetupComponent()
{
Button button = rootVisualElement.Q<Button>("UISetupComponentButton");
Assume.That(button, Is.Not.Null);
// Test steps.
// ...
}
}
Best practices
When creating custom UITestComponent classes:
- Keep components focused: Design each component to have a single, clear responsibility.
- Use appropriate lifecycle methods: Initialize resources in
Initialize()orBeforeTest(), clean up the state inAfterTest()orShutdown(). Don't forget to call the base function when overriding theInitialize()method. - Manage and check references: Be mindful of checking for null references and cleaning up the state properly between tests.