|
| 1 | +--- |
| 2 | +uid: writing-csharp-tests |
| 3 | +title: Writing tests in C# for Blazor components |
| 4 | +--- |
| 5 | + |
| 6 | +# Writing tests in C# for Blazor components |
| 7 | + |
| 8 | +Testing Blazor components is a different from testing regular C# classes: Blazor components are *rendered*, they have the *Blazor component life-cycle*, during which we can *provide input* to them and where they *produce output*. |
| 9 | + |
| 10 | +**bUnit** enables you to render the component you want to test, pass in parameters to it, inject services into it, and access the rendered component instance and the markup it has produced. |
| 11 | + |
| 12 | +Rendering a component happens through bUnit's <xref:Bunit.TestContext>, and the result of the rendering, a <xref:Bunit.IRenderedComponent`1>, provides access to the component instance, and the markup produced by the component. |
| 13 | + |
| 14 | +## Creating a basic test |
| 15 | + |
| 16 | +Lets see a simple example, where we test the following `<HelloWorld>` component: |
| 17 | + |
| 18 | +[!code-cshtml[HelloWorld.razor](../../samples/components/HelloWorld.razor)] |
| 19 | + |
| 20 | +# [xUnit](#tab/xunit) |
| 21 | + |
| 22 | +[!code-csharp[HelloWorldTest.cs](../../samples/tests/xunit/HelloWorldTest.cs)] |
| 23 | + |
| 24 | +# [NUnit](#tab/nunit) |
| 25 | + |
| 26 | +[!code-csharp[HelloWorldTest.cs](../../samples/tests/nunit/HelloWorldTest.cs)] |
| 27 | + |
| 28 | +> [!NOTE] |
| 29 | +> `TestContext` is an ambiguous reference between `TestContext` and `NUnit.Framework.TestContext`, so you have to specify the `Bunit` namespace when referencing `TestContext` to resolve the ambiguity for the compiler. Alternatively, you can give bUnit's `TestContext` a different name during import, e.g.: `using BunitTestContext = Bunit.TestContext;` |
| 30 | +
|
| 31 | +# [MSTest](#tab/mstest) |
| 32 | + |
| 33 | +[!code-csharp[HelloWorldTest.cs](../../samples/tests/mstest/HelloWorldTest.cs)] |
| 34 | + |
| 35 | +> [!NOTE] |
| 36 | +> `TestContext` is an ambiguous reference between `TestContext` and `Microsoft.VisualStudio.TestTools.UnitTesting.TestContext`, so you have to specify the `Bunit` namespace when referencing `TestContext` to resolve the ambiguity for the compiler. Alternatively, you can give bUnit's `TestContext` a different name during import, e.g.: |
| 37 | +> `using BunitTestContext = Bunit.TestContext;` |
| 38 | +
|
| 39 | +*** |
| 40 | + |
| 41 | +In this test, we do the following: |
| 42 | + |
| 43 | +1. New up the disposable <xref:Bunit.TestContext>, and assign it using the `using var` syntax, to avoid unnecessary indention. |
| 44 | +2. Render the `<HelloWorld>` component using <xref:Bunit.TestContext>, which we do through the <xref:Bunit.TestContext.RenderComponent``1(Bunit.Rendering.ComponentParameter[])> method. We will cover passing parameters to components elsewhere. |
| 45 | +3. Verify the rendered markup from the `<HelloWorld>` component using the <xref:Bunit.MarkupMatchesAssertExtensions.MarkupMatches(Bunit.IRenderedFragment,System.String,System.String)> method, which performs a semantic comparison of the expected markup with the rendered markup. |
| 46 | + |
| 47 | +> [!TIP] |
| 48 | +> Learn more about how the semantic HTML/markup comparison in bUnit work, and how to customize it on the <xref:semantic-html-comparison> page. |
| 49 | +
|
| 50 | +> [!TIP] |
| 51 | +> In bUnit tests, we like to use the abbreviation `CUT`, short for "component under test", to indicate the component that is being tested. This is inspired by the common testing abbreviation `SUT`, short for "system under test". |
| 52 | +
|
| 53 | +## Remove boilerplate code from tests |
| 54 | + |
| 55 | +We can remove some boilerplate code from each test by making the <xref:Bunit.TestContext> implicitly available to the test class, so we do not have to have `using var ctx = new Bunit.TestContext();` in every test. This can be done like this: |
| 56 | + |
| 57 | +# [xUnit](#tab/xunit) |
| 58 | + |
| 59 | +[!code-csharp[HelloWorldImplicitContextTest.cs](../../samples/tests/xunit/HelloWorldImplicitContextTest.cs)] |
| 60 | + |
| 61 | +Since xUnit instantiates test classes for each execution of test methods inside them and disposes them after each test method has run, we simply inherit from <xref:Bunit.TestContext>, and methods like <xref:Bunit.TestContext.RenderComponent``1(Bunit.Rendering.ComponentParameter[])> can now be called directly from each test, as seen in the listing above. |
| 62 | + |
| 63 | +# [NUnit](#tab/nunit) |
| 64 | + |
| 65 | +[!code-csharp[HelloWorldImplicitContextTest.cs](../../samples/tests/nunit/HelloWorldImplicitContextTest.cs)] |
| 66 | + |
| 67 | +[!code-csharp[BunitTestContext.cs](../../samples/tests/nunit/BunitTestContext.cs)] |
| 68 | + |
| 69 | +Since NUnit instantiates the test class is once, we cannot simply inherit directly from <xref:Bunit.TestContext>, as we want a fresh instance of <xref:Bunit.TestContext> for each test. Instead, we create a helper class, `BunitTestContext`, which is listed above, and use that to hook into NUnit's `[SetUp]` and `[TearDown]` methods, which runs before and after each test. |
| 70 | + |
| 71 | +Then methods like <xref:Bunit.TestContext.RenderComponent``1(Bunit.Rendering.ComponentParameter[])> can now be called directly from each test, as seen in the listing above. |
| 72 | + |
| 73 | +# [MSTest](#tab/mstest) |
| 74 | + |
| 75 | +[!code-csharp[HelloWorldImplicitContextTest.cs](../../samples/tests/mstest/HelloWorldImplicitContextTest.cs)] |
| 76 | + |
| 77 | +[!code-csharp[BunitTestContext.cs](../../samples/tests/mstest/BunitTestContext.cs)] |
| 78 | + |
| 79 | +Since MSTest instantiates the test class is once, we cannot simply inherit directly from <xref:Bunit.TestContext>, as we want a fresh instance of <xref:Bunit.TestContext> for each test. Instead, we create a helper class, `BunitTestContext`, which is listed above, and use that to hook into MSTest's `[TestInitialize]` and `[TestCleanup]` methods, which runs before and after each test. |
| 80 | + |
| 81 | +Then methods like <xref:Bunit.TestContext.RenderComponent``1(Bunit.Rendering.ComponentParameter[])> can now be called directly from each test, as seen in the listing above. |
| 82 | + |
| 83 | +*** |
| 84 | + |
| 85 | +## Further reading |
| 86 | + |
| 87 | +With the basics out of the way, we will next look at how to pass parameters and inject services into our components under test, and after that, cover in more details the various ways we can verify the outcome of a rendering. |
| 88 | + |
| 89 | +- <xref:passing-parameters-to-components> |
| 90 | +- <xref:inject-services-into-components> |
| 91 | +- <xref:verify-markup> |
| 92 | +- <xref:verify-component-state> |
| 93 | +- <xref:trigger-event-handlers> |
0 commit comments