{"id":2204,"date":"2023-09-12T12:35:05","date_gmt":"2023-09-12T12:35:05","guid":{"rendered":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/?p=2204"},"modified":"2023-09-12T12:35:06","modified_gmt":"2023-09-12T12:35:06","slug":"webuis-with-blazor-part-2","status":"publish","type":"post","link":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/","title":{"rendered":"Test-driven development for Blazor web components with bUnit"},"content":{"rendered":"\n<p>In the <a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-1\/\">previous article<\/a>, the Blazor framework was introduced, which can be used in conjunction with C# to create web UIs. In this second article, we will take a look at a testing library that is ranked in the top 10 NuGet downloads for Blazor&nbsp;<a href=\"#[1]\">[1]<\/a>.<\/p>\n\n\n\n<p>bUnit is a library used to create unit tests for Blazor components. Its stable version has been available since 2021&nbsp;<a href=\"#[2]\">[2]<\/a> and is recommended in Blazor\u2019s official documentation for component tests&nbsp;<a href=\"#[3]\">[3]<\/a>.<\/p>\n\n\n\n<p>The documentation for bUnit can be found at <a href=\"https:\/\/bunit.dev\/\">https:\/\/bunit.dev\/<\/a><\/p>\n\n\n\n<div style=\"height:10px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Razor syntax for a web UI using C# instead of JavaScript<\/h2>\n\n\n\n<p>Blazor apps are built entirely using Razor components with the file extension .razor. These components use Razor syntax to describe websites or parts of it. This syntax is made up of a mix of HTML, special Razor markup and C#. CSS is used for styling.<\/p>\n\n\n\n<p>The typical Blazor demo page with a counter looks like this:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@page \"\/counter\"\n\n&lt;PageTitle>Counter&lt;\/PageTitle>\n\n&lt;h1>Counter&lt;\/h1>\n\n&lt;p role=\"status\">Current count: @currentCount&lt;\/p>\n\n&lt;button class=\"btn btn-primary\" @onclick=\"IncrementCount\">Click me&lt;\/button>\n\n@code {\n    private int currentCount = 0;\n\n    private void IncrementCount()\n    {\n        currentCount++;\n    }\n}\n<\/pre>\n\n\n\n<p><em>Figure 1: Counter.razor page<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>In this example, HTML and C# are contained in the same file. For more complex components, it would be helpful to separate these by creating a code-behind file. The file for our <code>Counter.razor<\/code> component is then named <code>Counter.razor.cs<\/code>. The compiler automatically combines the two files so that IntelliSense can support us during programming.<\/p>\n\n\n\n<p>To keep the snippets of code in this article shorter, the markup and C# have been separated using a code-behind file in all of the examples.<\/p>\n\n\n\n<div style=\"height:10px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Components in Blazor: Razor components<\/h2>\n\n\n\n<p>To encapsulate elements and make them reusable, we execute functionalities in components. These only contain the markup and the code for an individual functionality. Then, only components are added to the website and any parameters and callbacks are linked. Components can be moved to a library and provided as NuGet packages.<\/p>\n\n\n\n<p>The example shows the same counter after it has been encapsulated in a <code>CounterComponent.razor<\/code> component. The result displayed in the browser is the same.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@page \"\/counter\"\n@using BlazorWasmTemplateApp.Components\n&lt;PageTitle>Counter&lt;\/PageTitle>\n\n&lt;h1>CounterComponent&lt;\/h1>\n&lt;CounterComponent\/>\n<\/pre>\n\n\n\n<p><em><em>Figure 2: <code>Counter.razor<\/code> page with component<\/em><\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;p id=\"currentCount\" role=\"status\">Current count: @currentCount&lt;\/p>\n\n&lt;button id=\"incrementCountButton\" class=\"btn btn-primary\" @onclick=\"IncrementCount\">Click me&lt;\/button>\n<\/pre>\n\n\n\n<p><em><em>Figure 3: Markup for <code>CounterComponent.razor <\/code>counter component<\/em><\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using Microsoft.AspNetCore.Components;\n\nnamespace BlazorWasmTemplateApp.Components\n{\n    public partial class CounterComponent : ComponentBase\n    {\n        private int currentCount = 0;\n\n        private void IncrementCount()\n        {\n            currentCount++;\n        }\n    }\n}\n\n<\/pre>\n\n\n\n<p><em><em>Figure 4: Code for <code>CounterComponent.razor.cs<\/code> counter component<\/em><\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Unit tests for the UI<\/h2>\n\n\n\n<p>Usually, an application\u2019s UI is checked using end-to-end tests (E2E). Frameworks such as Selenium and Playwright are used for this purpose in web development. These tests launch the entire application, render it in a browser and then carry out the test steps. In these types of tests, functionality is often verified through all layers of the application. Due to their complexity, E2E tests are often high-maintenance and slow. As a result, it is generally recommended that you only write a few E2E tests and lots of component tests (test pyramid, see <a href=\"#[4]\">[4]<\/a>).<\/p>\n\n\n\n<p>If you use test-driven development (TDD) principles for UI development or programme a user interface using extensive logic, you will need to carry out a lot of tests in a short period of time. As a consequence, there are disadvantages to exclusively using E2E tests. bUnit tests are the opposite of E2E tests \u2013 they give a good overview, are isolated from each other and, above all, are fast. It often only takes a few milliseconds to execute a test that feels the same as a back end unit test.<\/p>\n\n\n\n<p>bUnit is also a good choice if you are building a Razor component library. It is not without reason that the major sponsors of bUnit include companies like Progress Telerik and Syncfusion, which sell Blazor UI component libraries, among other things.<\/p>\n\n\n\n<p>Unit tests for UI that use bUnit are therefore a good addition to E2E testing, as they allow for details in the UI to be tested.<\/p>\n\n\n\n<div style=\"height:10px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Getting started with bUnit<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Preparations<\/h3>\n\n\n\n<p>bUnit is not a framework: it is dependent upon the xUnit, NUnit or MSTest test runners. xUnit is used for the examples in this article. As usual, the tests are run via either the IDE or the command line using <code>dotnet test<\/code>.<\/p>\n\n\n\n<p><br>Since we want to use bUnit to test Razor components, we need to change the SDK in the project file for our test project to <code>Microsoft.NET.Sdk.Razor<\/code>. Details on this can be found in the bUnit documentation at&nbsp;<a href=\"#[5]\">[5]<\/a>.<\/p>\n\n\n\n<p>bUnit is compatible with test projects in the .NET Standard 2.1 version and with all versions of .NET Core from 3.1 and up.<\/p>\n\n\n\n<p>Tests can be programmed in pure C# or Razor syntax. For the sake of clarity, only the variant that uses C# code is shown. When using Razor syntax, it is somewhat easier to pass parameters and callbacks as well as to directly compare HTML with an expected markup. However, this advantage of Razor syntax is irrelevant in practice as comparing HTML produces tests that are difficult to maintain.<\/p>\n\n\n\n<div style=\"height:10px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">The first test<\/h3>\n\n\n\n<p>What could a test of our <code>CounterComponent <\/code>look like? We want to check whether our counter shows 0 in its initial state and 1 after clicking.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using Bunit;\n\nnamespace Tests\n{\n    public class CounterComponentTests : TestContext\n    {\n\n        [Fact]\n        public void CurrentCount_IsZero_ByDefault()\n        {\n            \/\/Arrange\n            var componentUnderTest = RenderComponent&lt;CounterComponent>();\n\n            \/\/Act\n            AngleSharp.Dom.IElement paragraph = componentUnderTest.Find(\"#currentCount\");\n\n            \/\/Assert\n            Assert.Equal(\"Current count: 0\", paragraph.InnerHtml);\n        }\n\n        [Fact]\n        public void CurrentCount_Increases_WhenButtonClicked()\n        {\n            \/\/Arrange\n            var componentUnderTest = RenderComponent&lt;CounterComponent>();\n            AngleSharp.Dom.IElement button = componentUnderTest.Find(\"#incrementCountButton\");\n\n            \/\/Act\n            button.Click();\n\n            \/\/Assert\n            AngleSharp.Dom.IElement paragraph = componentUnderTest.Find(\"#currentCount\");\n            Assert.Equal(\"Current count: 1\", paragraph.InnerHtml);\n        }\n    }\n}\n<\/pre>\n\n\n\n<p><em>Figure 5: Simple <code>CounterComponent <\/code>test:<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Using the generic method <code>RenderComponent&lt;TComponent&gt;()<\/code> with <code>TComponent <\/code>as the component type to be tested creates a rendering. This rendering of our <code>CounterComponent<\/code>, showing its attributes, methods and properties, corresponds to how the components will be displayed in a browser.<\/p>\n\n\n\n<p>In order to perform interactions and check the content of the rendering, we can use the <code>Find() <\/code>method to identify elements. This method expects a <code>string CSS <\/code>selector to be a parameter. A CSS selector is a filter for the elements in an HTML DOM that can filter by CSS classes, attributes, IDs or types of HTML element. A good reference for CSS selectors can be found at <a href=\"#[6]\">[6]<\/a>.<\/p>\n\n\n\n<p>The &#8216;<code>#<\/code>&#8216; CSS selector filters by element ID. The <code>#currentCount<\/code> selector therefore filters out the element with <code>id=\"currentCount\"<\/code>, which contains the current value of the counter in our example component.<\/p>\n\n\n\n<p>To determine whether our test has been successful, we compare the HTML content with an expected text. Though these types of test are fragile and high-maintenance, they act as a good demo example.<\/p>\n\n\n\n<div style=\"height:10px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Testing components with parameters<\/h3>\n\n\n\n<p>What do tests for components with parameters look like? Here, we extend our <code>CounterComponent <\/code>using a parameter that enables us to set the starting value. As we want to develop our component using a test-driven approach, we add a new test first. In this test, we use an overload of the <code>RenderComponent()<\/code> method to pass parameters to a <code>ParameterBuilder <\/code>using a lambda expression parameter.<\/p>\n\n\n\n<p>As we are taking a test-driven approach and the <code>StartValue <\/code>parameter in our <code>CounterComponent <\/code>is not available yet, our test is not yet compilable.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[Fact]\npublic void CurrentCount_IsNotZero_WhenStartValueSet()\n{\n    \/\/Arrange\n    int startValue = 42;\n    var componentUnderTest = RenderComponent&lt;CounterComponent>(\n        parameterBuilder => parameterBuilder.Add(param => param.StartValue, startValue));\n\n    \/\/Act\n    AngleSharp.Dom.IElement paragraph = componentUnderTest.Find(\"#currentCount\");\n\n    \/\/Assert\n    Assert.Equal(\"Current count: \" + startValue, paragraph.InnerHtml);\n}\n<\/pre>\n\n\n\n<p><em>Figure 6: Test for our component with a parameter<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>To make the test compilable, we have to extend our <code>CounterComponent<\/code>component. In the first step, we add the <code>StartValue<\/code>property of the <code>int<\/code> type and assign it the parameter attribute. Now, our components can be made compilable. Next, we run our new test and can see that it has failed. We are still in the red phase of the red, green, refactor cycle of TDD and we need to adapt the implementation. To do this, we initialise our <code>currentCount<\/code> variable in the <code>OnParametersSet()<\/code> method. This method is called by Blazor after the parameters have been passed to a component. Now, we can run the test again and see that it has been successful. We are therefore in the green phase of our TDD cycle. We do not make any changes in the refactoring phase of a TDD cycle<\/p>\n\n\n\n<p>The code-behind file for our component now looks as follows:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using Microsoft.AspNetCore.Components;\n\nnamespace BlazorWasmTemplateApp.Components\n{\n    public partial class CounterComponent : ComponentBase\n    {\n        [Parameter]\n        public int StartValue { get; set; } = 0;\n\n        private int currentCount = 0;\n\n        protected override void OnParametersSet()\n        {\n            currentCount = StartValue;\n        }\n\n        private void IncrementCount()\n        {\n            currentCount++;\n        }\n    }\n}\n\n<\/pre>\n\n\n\n<p><em>Figure 7: Component extended by one parameter<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Testing components with events<\/h3>\n\n\n\n<p>Another typical element of Razor components is events. Let\u2019s assume that our website would like to be informed if the value on our counter changes. To do so, we expand our tests once more using the following test:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[Fact]\npublic void OnCountIncremented_WasInvoked_WhenButtonClicked()\n{\n    \/\/Arrange\n    bool isEventInvoked = false;\n    var componentUnderTest = RenderComponent&lt;CounterComponent>(\n        parameterBuilder => parameterBuilder\n.Add(param => param.OnCountIncremented, () => isEventInvoked = true));\n\n    AngleSharp.Dom.IElement button = componentUnderTest.Find(\"#incrementCountButton\");\n\n    \/\/Act\n    button.Click();\n\n    \/\/Assert\n    Assert.True(isEventInvoked);\n}\n<\/pre>\n\n\n\n<p><em>Figure 8: Test for our component with an event<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Our test is not yet compilable here either. As a result, we add to our component. We add the new <code>OnCountIncremented<\/code> property of the <code>EventCallback<\/code> type and assign it the parameter attribute as normal. Our test is now compilable, but it still fails. To ensure that our new test is successful in the next step, we invoke the callback in the <code>IncrementCount()<\/code> method of our component.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using Microsoft.AspNetCore.Components;\n\nnamespace BlazorWasmTemplateApp.Components\n{\n    public partial class CounterComponent : ComponentBase\n    {\n        [Parameter]\n        public int StartValue { get; set; } = 0;\n\n        [Parameter]\n        public EventCallback OnCountIncremented { get; set; }\n\n        private int currentCount = 0;\n\n        protected override void OnParametersSet()\n        {\n            currentCount = StartValue;\n        }\n\n        private async Task IncrementCount()\n        {\n            currentCount++;\n            await OnCountIncremented.InvokeAsync();\n        }\n    }\n}\n\n<\/pre>\n\n\n\n<p><em>Figure 9: Component extended by an event<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Subcomponents<\/h3>\n\n\n\n<p>Razor components can contain Razor components within themselves (subcomponents). It is possible to use bUnit to find these subcomponents in DOM, assign parameters to them, re-render them and execute asserts. Whether these tests are a good idea depends on the scenario. bUnit makes it possible to replace components with test doubles in order to avoid dependencies. This can be particularly useful when using libraries from third-party providers.<\/p>\n\n\n\n<div style=\"height:11px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Dependency injection<\/h3>\n\n\n\n<p>In addition to parameters and events, it is common practice with Razor components to inject dependencies by means of dependency injection. The Blazor framework uses the dependency injection in ASP.NET Core. bUnit therefore makes the <code>Services<\/code> collection available in the <code>TestContext<\/code> base class, known from ASP.NET Core. Test doubles can be registered there prior to rendering the component. For dependencies such as <code>IJsRuntime<\/code>, <code>HttpClient<\/code>, <code>NavigationManager<\/code>, etc., which are normally automatically assigned to the components by ASP.NET Core, there are instructions for test doubles in the bUnit documentation.<\/p>\n\n\n\n<div style=\"height:10px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>bUnit also provides a way for Blazor components to be examined in isolation, quickly and without E2E testing. This provides Blazor developers with an important tool for ensuring the serviceability of the Blazor app through testing. The UI can also be developed using a test-driven approach. In this respect, Blazor is on an equal footing with other SPA frameworks.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Sources<\/h2>\n\n\n\n<p id=\"[1]\">[1] nuget.org. [Online]. Available: <a href=\"https:\/\/www.nuget.org\/packages?q=Tags%3A%22Blazor%22&amp;sortby=totalDownloads-desc\">https:\/\/www.nuget.org\/packages?q=Tags%3A%22Blazor%22&amp;sortby=totalDownloads-desc<\/a>. [Zugriff am 22 Juni 2023].<\/p>\n\n\n\n<p id=\"[2]\">[2] E. Hansen, \u201ebUnit Releases,\u201c GitHub, [Online]. Available: <a href=\"https:\/\/github.com\/bUnit-dev\/bUnit\/releases?page=3\">https:\/\/github.com\/bUnit-dev\/bUnit\/releases?page=3<\/a>. [Zugriff am 22 Juni 2023].<\/p>\n\n\n\n<p id=\"[3]\">[3] L. Latham, R. Anderson und GitHubPang, \u201eTest Razor components in ASP.NET Core Blazor,\u201c Microsoft Learn, 04 April 2023. [Online]. Available: <a href=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/blazor\/test\">https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/blazor\/test<\/a>. [Zugriff am 22 Juni 2023].<\/p>\n\n\n\n<p id=\"[4]\">[4] M. Cohn, Succeeding with Agile: Software Development Using Scrum, Addison-Wesley, 2009.<\/p>\n\n\n\n<p id=\"[5]\">[5] E. Hansen, \u201eCreating a new bUnit test project,\u201c bUnit, 7 April 2023. [Online]. Available: <a href=\"https:\/\/bunit.dev\/docs\/getting-started\/create-test-project.html\">https:\/\/bunit.dev\/docs\/getting-started\/create-test-project.html<\/a>. [Zugriff am 22 Juni 2023].<\/p>\n\n\n\n<p id=\"[6]\">[6] Refsnes Data, \u201eCSS Selector Reference,\u201c W3Schools, [Online]. Available: <a href=\"https:\/\/www.w3schools.com\/cssref\/css_selectors.php\">https:\/\/www.w3schools.com\/cssref\/css_selectors.php<\/a>. [Zugriff am 22 Juni 2023].<\/p>\n\n\n\n<p id=\"[7]\">[7] E. Hansen, \u201ebUnit Documentation,\u201c 2023. [Online]. Available: <a href=\"https:\/\/bunit.dev\/docs\/getting-started\/index.html\">https:\/\/bunit.dev\/docs\/getting-started\/index.html<\/a>. [Zugriff am 22 Juni 2023].<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n","protected":false},"excerpt":{"rendered":"<p>The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.<\/p>\n","protected":false},"author":164,"featured_media":2189,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","footnotes":""},"categories":[805],"tags":[394,828,888,889,890,893,894,895],"topics":[892],"class_list":["post-2204","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-manufacturing-solutions","tag-ui","tag-c","tag-blazor","tag-hosting-modelle","tag-interaktive-web-frontends","tag-industrie","tag-web-frontends","tag-web-uis","topics-web-uis-with-blazor"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.0 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>ZEISS Digital Innovation Blog - Test-driven development for Blazor web components with bUnit<\/title>\n<meta name=\"description\" content=\"The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Test Driven Development f\u00fcr Blazor-Webkomponenten mit bUnit - Teil 2\" \/>\n<meta property=\"og:description\" content=\"The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/\" \/>\n<meta property=\"og:site_name\" content=\"Digital Innovation Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-09-12T12:35:05+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-09-12T12:35:06+00:00\" \/>\n<meta name=\"author\" content=\"Michael Ro\u00dfberg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"Test Driven Development f\u00fcr Blazor-Webkomponenten mit bUnit - Teil 2\" \/>\n<meta name=\"twitter:description\" content=\"The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Michael Ro\u00dfberg\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/\",\"name\":\"ZEISS Digital Innovation Blog - Test-driven development for Blazor web components with bUnit\",\"isPartOf\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2023\/08\/Beitragsbild.jpg\",\"datePublished\":\"2023-09-12T12:35:05+00:00\",\"dateModified\":\"2023-09-12T12:35:06+00:00\",\"author\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/f861d1528312de4bf45efe87dbcec45e\"},\"description\":\"The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.\",\"breadcrumb\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#primaryimage\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2023\/08\/Beitragsbild.jpg\",\"contentUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2023\/08\/Beitragsbild.jpg\",\"width\":2000,\"height\":1333},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Test-driven development for Blazor web components with bUnit\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#website\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/\",\"name\":\"Digital Innovation Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/f861d1528312de4bf45efe87dbcec45e\",\"name\":\"Michael Ro\u00dfberg\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2023\/08\/Bewerbungsfoto_quadratisch-150x150.jpg\",\"contentUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2023\/08\/Bewerbungsfoto_quadratisch-150x150.jpg\",\"caption\":\"Michael Ro\u00dfberg\"},\"description\":\"Michael Rossberg is a software developer at ZEISS Digital Innovation. He works primarily with .NET in the backend. Additionaly, he brings his knowledge from a degree in automation technology and five years as automation engineer to the Manufacturing Solutions area.\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/author\/enmichaelrossberg\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"ZEISS Digital Innovation Blog - Test-driven development for Blazor web components with bUnit","description":"The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/","og_locale":"en_US","og_type":"article","og_title":"Test Driven Development f\u00fcr Blazor-Webkomponenten mit bUnit - Teil 2","og_description":"The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.","og_url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/","og_site_name":"Digital Innovation Blog","article_published_time":"2023-09-12T12:35:05+00:00","article_modified_time":"2023-09-12T12:35:06+00:00","author":"Michael Ro\u00dfberg","twitter_card":"summary_large_image","twitter_title":"Test Driven Development f\u00fcr Blazor-Webkomponenten mit bUnit - Teil 2","twitter_description":"The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.","twitter_misc":{"Written by":"Michael Ro\u00dfberg","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/","name":"ZEISS Digital Innovation Blog - Test-driven development for Blazor web components with bUnit","isPartOf":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#primaryimage"},"image":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2023\/08\/Beitragsbild.jpg","datePublished":"2023-09-12T12:35:05+00:00","dateModified":"2023-09-12T12:35:06+00:00","author":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/f861d1528312de4bf45efe87dbcec45e"},"description":"The bUnit library can be used to create unit tests for Blazor web front-ends. This allows you to create many tests that can be run quickly. Examples are used to present the first steps and the most important features of bUnit.","breadcrumb":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#primaryimage","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2023\/08\/Beitragsbild.jpg","contentUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2023\/08\/Beitragsbild.jpg","width":2000,"height":1333},{"@type":"BreadcrumbList","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/webuis-with-blazor-part-2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/"},{"@type":"ListItem","position":2,"name":"Test-driven development for Blazor web components with bUnit"}]},{"@type":"WebSite","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#website","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/","name":"Digital Innovation Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/f861d1528312de4bf45efe87dbcec45e","name":"Michael Ro\u00dfberg","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/#\/schema\/person\/image\/","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2023\/08\/Bewerbungsfoto_quadratisch-150x150.jpg","contentUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-content\/uploads\/sites\/3\/2023\/08\/Bewerbungsfoto_quadratisch-150x150.jpg","caption":"Michael Ro\u00dfberg"},"description":"Michael Rossberg is a software developer at ZEISS Digital Innovation. He works primarily with .NET in the backend. Additionaly, he brings his knowledge from a degree in automation technology and five years as automation engineer to the Manufacturing Solutions area.","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/author\/enmichaelrossberg\/"}]}},"author_meta":{"display_name":"Michael Ro\u00dfberg","author_link":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/author\/enmichaelrossberg\/"},"featured_img":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2023\/08\/Beitragsbild-600x400.jpg","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">Manufacturing Solutions<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">Manufacturing Solutions<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">UI<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">Blazor<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">Hosting-Modelle<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">interaktive Web-Frontends<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">Industrie<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">Web-Frontends<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/category\/manufacturing-solutions\/\" class=\"advgb-post-tax-term\">Web-UIs;<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">UI<\/span>","<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">Blazor<\/span>","<span class=\"advgb-post-tax-term\">Hosting-Modelle<\/span>","<span class=\"advgb-post-tax-term\">interaktive Web-Frontends<\/span>","<span class=\"advgb-post-tax-term\">Industrie<\/span>","<span class=\"advgb-post-tax-term\">Web-Frontends<\/span>","<span class=\"advgb-post-tax-term\">Web-UIs;<\/span>"]}},"comment_count":"0","relative_dates":{"created":"Posted 3 years ago","modified":"Updated 3 years ago"},"absolute_dates":{"created":"Posted on September 12, 2023","modified":"Updated on September 12, 2023"},"absolute_dates_time":{"created":"Posted on September 12, 2023 12:35 pm","modified":"Updated on September 12, 2023 12:35 pm"},"featured_img_caption":"","series_order":"","_links":{"self":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/2204","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/users\/164"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/comments?post=2204"}],"version-history":[{"count":11,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/2204\/revisions"}],"predecessor-version":[{"id":2263,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/posts\/2204\/revisions\/2263"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/media\/2189"}],"wp:attachment":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/media?parent=2204"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/categories?post=2204"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/tags?post=2204"},{"taxonomy":"topics","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/en\/wp-json\/wp\/v2\/topics?post=2204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}