API Documentation
- Startup
- Test Setup
- Test Checks
- UI Automation
- Search for Elements
CssPath(String selector, string containsText="", int index=-1)
ClickPath(String selector, string containsText="", int index=-1)
Find(string selector, string withText="")
FindAll(string selector, string withText="")
WaitForElement(string selector, string withText="", int timeout=5000, int interval=200)
- Manipulate Elements
- Wait
- Search for Elements
Startup
IcuConfig
IcuConfig settings can be divided into System & Runtime parameters. System parameters are global startup properties that typically do not change. Runtime parameters can be changed at run time with the IcuBlazor Settings UI. These values are stored in the browsers LocalStorage and persist across sessions.
System Parameters
TestDir
(Default: "icu_data")
Directory underwwwroot
where test data files are stored.EnableServer
(Default: true)
IcuBlazor uses a local server to read & write test files. But in some scenarios a local server is problematic (e.g. running tests on a non-localhost site). SettingEnableServer=false
will effectively disable the IcuBlazor server allowing you to run a standalone CSB app.Editor
(Default: Editor.VisualStudio)
Editor to open when source code link is clicked.SourceDir
(Default: @"C:\\")
Path that distinguises your source files form others in stacktraces (e.g. @"c:\work\MySrc").Verbosity
(Default: LogLevel.Info)
Sets the logging level for IcuBlazor output.Name
(Default: "All Tests")
Name for the top Test Suite.IcuServer
IcuBlazor server URL.IcuServer
is only needed for standalone Client-Side Blazor apps.
Runtime Parameters
Filter
(Default: "")
UseFilter
to narrow down the list of tests run. TypicallyFilter
is changed through the IcuBlazor toolbar.If
Filter=""
then nominally all tests are run. You can exclude some tests (e.g. health & monitor checks) from the "all" category by tagging methods or suites with[TestTags("isolate")]
attributeYou can filter on tags with
#tag-search
or by suite$suite-search
. A filter that does not start with#
or$
will search across test method names.The filter value can also be set in the url query (e.g.
/MyTests/AllTests?filter=mymethod
). Note: Tag queries likefilter=#tag
are problematic. Usefilter=%23tag
instead.Interactive
(Default: false)
Pause execution and show verify dialog when a visual test fails.StopOnFirstFailure
(Default: false)
Stop test execution when the first check fails otherwise IcuBlazor will continue running tests.
AddIcuBlazor(IcuConfig cfg)
To configure IcuBlazor servers you will typically need something like this:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddIcuBlazor(new IcuConfig {
TestDir = @"my_test_data",
Verbosity = LogLevel.Error,
Editor = Editor.VisualStudioCode,
});
...
}
For Client-Side Blazor apps (CSBs) you will need to specify the server or set EnableServer=false
:
public static async Task Main(string[] args)
{
...
var baseAddr = builder.HostEnvironment.BaseAddress;
builder.Services.AddIcuBlazor(new IcuConfig {
IcuServer = baseAddr,
//EnableServer = false, // default is true
});
...
}
UseIcuServer()
The following will configure IcuBlazor servers.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseIcuServer(env);
...
}
Test Setup
<IcuTestSuite/>
<IcuTestSuite/>
is basically a container for your test methods.
@page "/MyTests/TestExample"
@inherits IcuBlazor.IcuTestSuite
@code {
public void Test_SimpleChecks()
{
Check().True(2 < 3, "a true test");
Check().Equal(6*9, 42, "What's the question?");
}
void Test_Hello()
{
var (a, b) = ("Hello", "World");
Check().Equal(a + b, "Hello World", "Not quite right");
}
}
AddTests()
It is rare that you will need this function because, by default, IcuBlazor uses Reflection and convention to automatically add tests within a suite. However, you can change this default by overriding IcuTestSuite.AddTests()
. For example:
@page "/MyTests/TestExample"
@inherits IcuBlazor.IcuTestSuite
@code {
:
public override void AddTests()
{
//base.AddTests(); // default: adds tests using Reflection
Suite("My Test Ordering");
Test("Should Pass", SimpleChecks);
Test("Should Fail", ShouldFail);
TestTask("Async task", TestAsyncMethod);
Test("Anonymous Test", (c) => {
c.True(10 > 2, "Anonymous function works");
});
TestTask("Async Anonymous test", async (c) => {
await Task.Delay(200);
c.True(true, "Inline async tests");
});
}
}
Suite(string path)
Start a new Test Suite list definition.
Test(string testName, Action<Checker> testf)
Add a test to the current Suite.
TestTask(string testName, Func<Checker,Task> testf)
Add an async test to the current Suite.
OnSuiteStart()
and OnSuiteEnd()
IcuBlazor provides hooks so that you can initialize and cleanup test suites.
public override void OnSuiteStart() {
base.OnSuiteStart();
emailer = getEmailService();
}
public override void OnSuiteEnd() {
emailer.Dispose();
base.OnSuiteEnd();
}
<IcuTestViewer/>
<IcuTestViewer/>
is a container for a list of IcuTestSuites
. For Example:
<IcuTestViewer Width="1000">
<TestExample />
<TestSystemInfo />
</IcuTestViewer>
It's often convenient to leave the contents of <IcuTestViewer>
empty. This will cause IcuBlazor to automatically embed all available test suites (i.e. classes that inherit from IcuTestSuite
).
<IcuTestDiv/>
<IcuTestDiv/>
is just a div where you can arrange your UI for testing. As a div, it can contain any html content and can have any CSS style. By default, it has the id #icu-test-div
which corresponds to the default selector in Check().Div()
. For testing consistency, you must provide a Width
argument.
<IcuTestDiv Suite="@this" Width="300" style="border:5px solid #ccc;">
<p>Any Html or Blazor content!</p>
<WeatherCounter @ref="myComponent"/>
</IcuTestDiv>
Test Checks
The following methods are accessed through the IcuTestSuite.Check()
function. Calling Check()
repeatedly is good practice because it registers the line number the check occurred on which is useful for Editor
integration.
Basic Checks
- True
- False
- Fail
- Equal
- NotEqual
- Skip
Visual Checks
- Text
- Log
- Div
Basic Unit Test Checks
IcuBlazor supports traditional unit test checks.
True(bool state, string message)
Check that state
is true. The message
is a description of the test.
False(bool state, string message)
Check that state
is false. The message
is a description of the test.
Fail(string message)
Just declare a failed test. For example:
try {
var x = 1;
var v = 10/(1-x);
Check().Fail("Should throw div by zero exception.");
} catch (Exception) {
// Nothing to see here. Carry on.
}
Equal<T>(T expected, T actual, string message)
Check if two primitive values (expected & actual) are equal.
NotEqual<T>(T expected, T actual, string message)
Check if two primitive values (expected & actual) are not equal.
Skip(string message)
Skip a test but display a warning message in the test viewer.
New IcuBlazor Checks
IcuBlazor offers some modern testing techniques. Some methods require storing test data for later use. Typically these test files are stored under a dir named "wwwwroot\{IcuConfig.TestDir}\{suite.SaveTestDir}\"
. Both SaveTestDir
and TestDir
are configurable at run time. SaveTestDir
defaults to suite class name. This allows you to reuse file names (e.g. "init") across suites.
Text(string expect, string result, string message, int limit=800)
Check that two strings are the same. An error is raised if the strings are larger than the limit
.
File(string logName, string result, string message, int limit=3000)
Check that a string is the same as the last saved string. The last string is saved in a file called "{logName}.txt"
. To keep the test from growing too large an error is raised if the strings are larger than the limit
. The logName
should be unique within it's IcuTestSuite
Div(string testName, string selector="#icu-test-div")
Take a snapshot of a div and compare with previous snapshot. The previous snapshot is saved in a file called "{testName}.png"
. The testName
should be unique within it's IcuTestSuite
await Check().Div(@"init");
await Check().Div(@"docs\awesome_setup1", "#holder");
await Check().Div(@"Firefox\billg\login-message", "#login .msg");
UI Automation
UI Automation involves searching for and manipulating HTML elements. Since UIs are deeply asynchronous, some waiting routines are also necessary.
- Search for Elements
- Wait
Search for Elements
CssPath(String selector, string containsText="", int index=-1)
CssPath
& ClickPath
are light-weight classes used to locate UI elements. Use selector
to specify a css path and containsText
to specify the text within the element. If multiple elements are found use index
to select one.
CssPath screen = new("#main"); // main div
ClickPath openBtn = new("button", "OK"); // OK button
CssPath entryTab = new(".tab-header > .tab", "", 2); // 3rd tab
ClickPath(String selector, string containsText="", int index=-1)
ClickPath
is a CssPath
that is clickable (e.g. a link or button)
and can be used in some apis like UI.Clicking()
.
Example: Click a dropdown item and capture result
CssPath detail = new(".main > .detail");
ClickPath dropbox = new(".my-combo");
async Task drop_select(Checker cx, string item, string hdrName)
{
// click dropbox & wait for dropItem to appear
ClickPath dropItem = new(".dropdown-item", item);
await UI.Clicking(dropbox).Shows(dropItem);
// click dropItem & wait for div (.header) to contain hdrName
var headerLabel = new CssPath(".header", hdrName);
await UI.Clicking(dropItem).Shows(headerLabel);
await Check().Div(hdrName+"_settings", detail);
}
Find(string selector, string withText="")
Find a single element that matches the selector
. Use withText
to only return elements that contain withText
.
var pwdInput = await UI.Find("#login .password");
var button = await UI.Find("button", "Cancel");
FindAll(string selector, string withText="")
Find all html elements that match the selector
. Use withText
to only return elements that contain withText
.
var inputs = await UI.FindAll("#login > *");
var buttons = await UI.FindAll("button");
WaitForElement(string selector, string withText="", int timeout=5000, int interval=200)
Wait for a single element to be added to DOM. Use selector
and withText
to specify which element to return.
public async Task Got_any_questions() {
await click_ok_button();
// wait for results to appear
var e = await UI.WaitForElement(".hdr-title", "question");
await Check().Div("got_question");
}
Manipulate Elements
TextContent(ElemRef e)
Get the text content of an html element
public async Task Got_any_questions() {
await click_ok_button();
// wait for results to appear
var dv = await UI.WaitForElement(".hdr-title", "question");
var html = await UI.HtmlContent(dv);
var text = await UI.TextContent(dv);
Check().File("MyComp_html", html, "Test div html content");
Check().File("MyComp_text", text, "Test div text content");
}
HtmlContent(ElemRef e)
Get the html content of an html element.
public async Task Got_any_questions() {
await click_ok_button();
// wait for results to appear
var dv = await UI.WaitForElement(".hdr-title", "question");
var html = await UI.HtmlContent(dv);
var text = await UI.TextContent(dv);
Check().File("MyComp_html", html, "Test div html content");
Check().File("MyComp_text", text, "Test div text content");
}
SetContent(ElemRef e, string html)
Get the html content of an html element.
public async Task Got_any_questions() {
var dv = await UI.Find(".mydiv");
await UI.SetContent(dv, "Mock <b style=\"background:tan\">Me!!</b>");
await Check().Div("mock_div");
await UI.SetContent(dv, Lorem.OfLength(447+2));
await Check().Div("lorem");
}
Click(ElemRef e)
Send a click event to an html element.
public async Task Test_Counter_UI()
{
var button = await UI.Find("button", "Hotter");
await UI.Click(button);
await UI.Click(button);
await Check().Div("click_twice");
}
SetValue(ElemRef e, string v)
Set html element.value = v. Typically used to enter text in input fields.
var pwd = await UI.Find("#password");
await UI.SetValue(pwd, "MrSnuggles");
Eval<T>(string code)
Execute javascript code and return the result as JSON. This is a catch-all for JS automation.
// find password field and set it.
var code = "document.getElementById('password').value='MrSnuggles'";
var res = await UI.Eval<string>(code);
Wait
Most UI systems are deeply asynchronous but tests require synchronization. IcuBlazor offers the following waiting helpers. The wait mechanism is a straightforward polling algorithm. It will poll every interval
milliseconds. If the wait condition is not satisfied after timeout
milliseconds an exception is thrown.
For<T>(Func<T> getter, int timeout=5000, int interval=200)
Wait by polling until getter()
returns a non-null value.
ForAsync<T>(Func<Task<T>> getter, int timeout=5000, int interval=200)
Wait by polling until an async getter()
returns a non-null value.
Until(Func<bool> isDone, int timeout=5000, int interval=200)
Wait until isDone()
is true.
UntilAsync(Func<Task<bool>> isDone, int timeout=5000, int interval=200)
Wait until an async isDone()
is true.