Crazor has a companion assembly called Crazor.Test which implements helper classes/methods to make it super easy to test the logic of your cards.
To start you create a MSTest test project
Add Crazor.Test and Crazor.Blazor nuget libraries..
nuget add package Crazor.Test
nuget add package Crazor.Blazor
-
Edit the .csproj to make it a aspnetcore project change
<Project Sdk="Microsoft.NET.Sdk">
to<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
-
add a Cards folder (should be a peer to Pages folder)
-
add Cards/_Imports.razor file containing this:
@using System.ComponentModel.DataAnnotations; @using AdaptiveCards @using Crazor @using Crazor.Blazor @using Crazor.Exceptions @using Crazor.Attributes @using System.Threading; @using System.Threading.Tasks; @using Crazor.Blazor.Components.Adaptive;
Now we will create a card and write a unit tests against the card.
-
Create a test card folder in cards/Foo
-
Create Card View (Blazor) - Create a Default.razor file in it
@inherits CardView <Card Version="1.5"> <TextBlock>Counter=@Counter</TextBlock> <ActionExecute Verb="@nameof(OnSubmit)"/> </Card> @code { public int Counter {get;set;} public void OnSubmit() => Counter++; }
-
Now create a unit test for it by creating a .cs file called TestFoo.cs and deriving your class for CardTest
using AdaptiveCards; using Crazor.Test; using Crazor.Test.MSTest; namespace MyTests { [TestClass] public class TestFoo : CardTest { [TestMethod] public async Task TestIncrementCounter() { // create an instance of the card by binding to it's route. await LoadCard("/Cards/Foo") // add assertion against the card .AssertTextBlock("Counter=0") // submit an action .ExecuteAction("OnSubmit") // write assertions agains the card that is returned .AssertTextBlock("Counter=1"); } } }
That's it! The pattern is essentially that you
- use base class method LoadCard(route) to instantiate a card.
- You write assertions against the card
- call ExecuteAction() to send input into the card.
- You write assertions against the card
Method | Description |
---|---|
LoadCard(route) | Load a card |
ExecuteAction(verb, data) | Invoke a verb (mimic a button click on an action). You can pass any arbitrary data payload to simulate input |
Method | Description |
---|---|
AssertTextBlock(text) | Assert there is a textblock with a text value |
AssertTextBlock(id, text) | Assert that TextBlock with Id has a text value |
AssertNoTextBlock(text) | Assert that there is no TextBlock with a text value |
AssertHas() | Assert there is an element of type T in the card |
AssertHas(id) | Assert there is an element of type T with id in the card |
AssertHasNo() | Assert there is no element of type T in the card |
AssertHasNo(id) | Assert there is no element of type T with id in the card. |
AssertElement(id, callback) | Find Element of type T and id and pass to the callback for custom assertion. |
AssertElements(callback) | Find all elements of type T and pass to the callback for custom assertion |
AssertCard(callback) | call callback with the card for custom assertions. |
All of the assertion methods are extensions to Task, so it is easy to create your own assertion helpers.
Here is the implementation of AssertTextBlock. You can see that you await the task to get the context. The context has
- Card - the adaptive card
- Services - The dependency injection services provider.
And it always returns the context back out so that the assertions can be chained together in a fluent style.
public static async Task<CardTestContext> AssertTextBlock(this Task<CardTestContext> contextTask, string id, string text)
{
var context = await contextTask;
var actual = context.Card.GetElements<AdaptiveTextBlock>().SingleOrDefault(el => el.Id == id)?.Text;
Assert.AreEqual(text, actual, $"TextBlock[{id}] Expected:'{text}' Actual:'{actual}'");
return context;
}
- Architecture - Describes overall structure of Crazor application
- Card Apps - How to create a CardApp class to define state and operations against state.
- Card Views - General information about Card Views
- Memory - Information on persistence and memory
- Validation - Model validation
- Routing - Information on customizing urls to support deep linking into cards
- Authentication - Authentication
- Unit tests - Writing unit tests for your cards.