Skip to content

Commit 9ff70f4

Browse files
committed
updates to docs and samples
1 parent 0a8eff1 commit 9ff70f4

File tree

4 files changed

+113
-12
lines changed

4 files changed

+113
-12
lines changed

docs/razor-examples.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
The library supports specifying the component under test and other markup/fragments using Razor syntax. The advantage is that we get Visual Studio help writing Razor and HTML, which is a much nicer experience than writing HTML in a string in a C# class/file. This is especially useful for more complex scenarios, where e.g. a component under test has many parameters or complex child contents.
44

5-
**NOTE:** This feature is _EXPERIMENTAL_ and syntax and API will likely changed.
5+
**NOTE:** This feature is _EXPERIMENTAL_ and syntax and API will likely changed. See [Contribute](readme.md/#contribute) for info on how to provide feedback and suggestions.
66

77
1. [Creating new test component](#creating-new-test-component)
8-
1. [Defining test cases](#defining-test-cases)
9-
1. [Known issues](#known-issues)
8+
2. [Defining test cases](#defining-test-cases)
9+
3. [Examples](#examples)
10+
4. [Known issues](#known-issues)
1011

1112
## Creating new test component
1213

@@ -77,7 +78,7 @@ The code above works as follows:
7778

7879
- Inside the `Test` methods you can do all the things you can in C#-based tests, e.g. assert against the CUT. The only difference is that some methods such as `TakeSnapshot()` is not available on the local scope, but through the `IRazorTestContext` object passed to each Test method.
7980

80-
### Examples
81+
## Examples
8182

8283
Here is a few examples that demonstrate how Razor test components can be used. More can be found in the [samples/tests/RazorComponentTests](../samples/tests/RazorComponentTests) samples folder.
8384

docs/snapshot-examples.md

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,81 @@
11
# Writing Snapshot Tests for Blazor Components
22

3-
TODO
3+
The library has basic support for snapshot testing, declared via Razor syntax in Razor files. In snapshot testing, you declare your input (e.g. one or more component under test) and the expected output, and the library will automatically tell you if they do not match.
4+
5+
Notable features that are missing at the moment is the ability to auto-generate the expected output and to trigger updates of expected output.
6+
7+
**NOTE:** This feature is _EXPERIMENTAL_ and syntax and API will likely changed. See [Contribute](readme.md/#contribute) for info on how to provide feedback and suggestions.
8+
9+
1. [Creating new snapshot test component](#creating-new-snapshot-test-component)
10+
2. [Defining snapshot test cases](#defining-snapshot-test-cases)
11+
3. [Examples](#examples)
12+
4. [Known issues](#known-issues)
13+
14+
## Creating new snapshot test component
15+
16+
To create Razor-based snapshot tests, we need to create snapshot test components.
17+
18+
All snapshot test components must inherit from `TestComponentBase`, e.g. by adding `@inherits TestComponentBase` to the top of your .razor file.
19+
20+
**Tip:** In the folder you keep your Razor-based snapshot tests, add a `_Imports.razor` file, and put `@inherits TestComponentBase` into that as well as any using statements you need. Then all snapshot test components inherits the correct base component by default and have the default imports available.
21+
22+
## Defining snapshot test cases
23+
24+
All you need to define a snapshot test case is the `<SnapshotTest>` component added to a test component, e.g.:
25+
26+
```cshtml
27+
<SnapshotTest Description="Helpful description of the test case - displayed if test fails"
28+
Setup="ctx => ctx.Services.AddMockJsRuntime()">
29+
<TestInput><!-- Declare your test input here, e.g. one or more components --></TestInput>
30+
<ExpectedOutput><!-- Declare your expected output here --></ExpectedOutput>
31+
</SnapshotTest>
32+
```
33+
34+
You can add as many `<SnapshotTest>` components to a test component as you want. Each `<SnapshotTest>` component will go through this life cycle:
35+
36+
1. Create a new test context (of type `ITestContext`).
37+
2. Call the `Setup` method, if specified, and pass it the test context. Use the `Setup` method to e.g. configure services, like registering a mock `IJsRuntime`.
38+
3. Render the child content of the `<TestInput>` component and capture its output.
39+
4. Render the child content of the `<ExpectedOutput>` component and capture its output.
40+
5. Verify that the two outputs are equal. If they are not, the test will fail with an `HtmlEqualException`.
41+
42+
## Examples
43+
44+
The following example shows how to test the the [TodoList.razor](../sample/src/Pages/TodoList.razor) component:
45+
46+
```cshtml
47+
@inherits TestComponentBase
48+
49+
<SnapshotTest Description="A todolist with one todo added should render correctly"
50+
Setup="ctx => ctx.Services.AddMockJsRuntime()">
51+
<TestInput>
52+
<TodoList Label="My label" Items=@(new Todo[]{ new Todo{ Id=42, Text="Check out this new thing called Blazor" } })>
53+
<ItemsTemplate Context="todo">
54+
<TodoItem Todo="todo"></TodoItem>
55+
</ItemsTemplate>
56+
</TodoList>
57+
</TestInput>
58+
<ExpectedOutput>
59+
<form>
60+
<div class="input-group">
61+
<input type="text" class="form-control" placeholder="My label" aria-label="My label" value="" />
62+
<div class="input-group-append">
63+
<button class="btn btn-secondary" type="submit">Add task</button>
64+
</div>
65+
</div>
66+
</form>
67+
<ol class="list-group">
68+
<li id:regex="todo-42" class="list-group-item list-group-item-action">
69+
<span>Check out this new thing called Blazor</span>
70+
<span class="float-right text-danger">(click to complete)</span>
71+
</li>
72+
</ol>
73+
</ExpectedOutput>
74+
</SnapshotTest>
75+
```
76+
77+
## Known issues
78+
79+
These are the known issues:
80+
81+
1. The xUnit test runner is not able to distinguish the individual `<SnapshotTest>`'s from each other. So they are all executed together, one at the time. This is less than a problem with snapshot tests than with Razor based tests, since a failing test will have its description printed along with the differences found in the snapshot. The solution is to develop a custom test runner, see [issue #4 - Create custom xUnit test runner/discoverer](/issues/4) for more details.

sample/tests/RazorTestComponents/Components/TodoListTest.razor

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
@using Xunit
33

44
<Fixture Setup="ctx => ctx.Services.AddMockJsRuntime()"
5-
Tests="new Test[]{ EmptyTodoList, SettingLabel, TaskListRendersItemsUsingItemTemplate }">
5+
Test="EmptyTodoList"
6+
Tests="new Test[]{ SettingLabel, TaskListRendersItemsUsingItemTemplate }">
67
<ComponentUnderTest>
78
<TodoList>
89
<ItemsTemplate>
@@ -31,17 +32,24 @@
3132

3233
void EmptyTodoList(IRazorTestContext context)
3334
{
35+
// Act - get the CUT
3436
var cut = context.GetComponentUnderTest<TodoList>();
3537

36-
cut.ShouldBe(context.GetFragment("EmptyTodoListRender"));
38+
// Assert - get the expected initial rendered HTML from the fragment
39+
// and use it to verify the initial rendered HTML
40+
var expectedInitialRender = context.GetFragment("EmptyTodoListRender");
41+
cut.ShouldBe(expectedInitialRender);
3742
}
3843

3944
void SettingLabel(IRazorTestContext context)
4045
{
46+
// Arrange - get the CUT
4147
var cut = context.GetComponentUnderTest<TodoList>();
4248

49+
// Act - update label
4350
cut.SetParametersAndRender((nameof(TodoList.Label), "LABEL"));
4451

52+
// Assert - verifiy that the placeholder and aria-label has changed
4553
cut.GetChangesSinceFirstRender().ShouldAllBe(
4654
diff => diff.ShouldBeAttributeChange("placeholder", "LABEL"),
4755
diff => diff.ShouldBeAttributeChange("aria-label", "LABEL")
@@ -50,14 +58,17 @@
5058

5159
void TaskListRendersItemsUsingItemTemplate(IRazorTestContext context)
5260
{
61+
// Arrange - get the cut and take a snapshot of the current render tree output
5362
var cut = context.GetComponentUnderTest<TodoList>();
5463
cut.TakeSnapshot();
5564

65+
// Act - assign test todo items to the CUT
5666
cut.SetParametersAndRender((nameof(TodoList.Items), TestItems));
5767

68+
// Assert - get the diffs since the snapshot and compare to the expected.
5869
var diffs = cut.GetChangesSinceSnapshot();
59-
diffs.ShouldHaveSingleChange()
60-
.ShouldBeAddition(context.GetFragment("TodoItemRender"));
70+
var expected = context.GetFragment("TodoItemRender");
71+
diffs.ShouldHaveSingleChange().ShouldBeAddition(expected);
6172
}
6273
}
6374

@@ -86,44 +97,55 @@
8697

8798
void OnFirstRenderInputFieldGetsFocus(IRazorTestContext context)
8899
{
100+
// Act
89101
var cut = context.GetComponentUnderTest<TodoList>();
90102

91-
// assert that there is a call to document.body.focus.call with a single argument,
103+
// Assert that there is a call to document.body.focus.call with a single argument,
92104
// a reference to the input element.
93105
jsRtMock.VerifyInvoke("document.body.focus.call")
94106
.Arguments.Single().ShouldBeElementReferenceTo(cut.Find("input"));
95107
}
96108

97109
void AfterFirstRenderInputFieldDoesntGetFocusAfterRerenders(IRazorTestContext context)
98110
{
111+
// Arrange
99112
var cut = context.GetComponentUnderTest<TodoList>();
100113

114+
// Act
101115
cut.Render(); // second render
102116
cut.Render(); // thrid render
103117
cut.Render(); // ...
104118
cut.Render();
105119

120+
// Assert that focus logic only runs on first render (only called 1 time).
106121
jsRtMock.VerifyInvoke("document.body.focus.call", calledTimes: 1);
107122
}
108123

109124
void WhenAddTaskFormIsSubmittedWithNoTextOnAddingTodoIsNotCalled(IRazorTestContext context)
110125
{
126+
// Arrange
111127
var cut = context.GetComponentUnderTest<TodoList>();
112128

129+
// Act - submit the empty form
113130
cut.Find("form").Submit();
114131

132+
// Assert - verify that no task was created
115133
Assert.Null(createdTodo);
116134
}
117135

118136
void WhenAddTaskFormIsSubmittedWithTextOnAddingTodoIsCalled(IRazorTestContext context)
119137
{
138+
// Arrange - ensure createdTask is null
120139
createdTodo = null;
121140
var cut = context.GetComponentUnderTest<TodoList>();
122141
var taskValue = "HELLO WORLD TASK";
123142

143+
// Act - find input field and change its value, then submit the form
124144
cut.Find("input").Change(taskValue);
125145
cut.Find("form").Submit();
126146

147+
// Assert that a new task has been passed to the EventCallback listener and that the
148+
// new task has the expected value
127149
Assert.NotNull(createdTodo);
128150
Assert.Equal(taskValue, createdTodo?.Text);
129151
}

sample/tests/SnapshotTests/TodoListTest.razor

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
</div>
2020
</form>
2121
<ol class="list-group">
22-
<li id:regex="todo-[\d]+" class="list-group-item list-group-item-action">
23-
<span diff:regex>[\w\d]+</span>
22+
<li id:regex="todo-42" class="list-group-item list-group-item-action">
23+
<span>Check out this new thing called Blazor</span>
2424
<span class="float-right text-danger">(click to complete)</span>
2525
</li>
2626
</ol>

0 commit comments

Comments
 (0)