1.0.0 - preview 01
[1.0.0-preview-01] - 2020-12-24
Happy holidays everyone. This release marks the first preview release of bUnit, which means that only small features additions and bugfixes will follow before version 1.0.0 is release soon.
Yet again many folks in the community have contributes by raising issues, answering questions, sending in pull requests and taking part in design discussions. A huge thanks to all of you. Also, a huge thanks to the sponsors of the project.
The following section list all changes in 1.0.0 preview 01.
Added
List of new features.
-
Added support for casting
BUnitJSRuntime
toIJSInProcessRuntime
andIJSUnmarshalledRuntime
. By @KristofferStrube in #279 -
Added support for triggering
@ontoggle
event handlers through a dedicatedToggle()
method. By @egil in #256. -
Added out of the box support for
<Virtualize>
component. When a<Virtualize>
component is used in a component under test, it's JavaScript interop-calls are faked by bUnits JSInterop, and it should result in all items being rendered immediately. By @egil in #240. -
Added support for components that call
ElementReference.FocusAsync
. These calls are handled by the bUnits JSInterop, that also allows you to verify thatFocusAsync
has been called for a specific element. For example, if a component has rendered an<input>
element, then the following code will verify that it has been focused usingFocusAsync
:var cut = RenderComponent<FocusingComponent>(); var input = cut.Find("input"); JSInterop.VerifyFocusAsyncInvoke() .Arguments[0] // the first argument is the ElemenetReference .ShouldBeElementReferenceTo(input);
-
Added
Render(RenderFragment)
andRender<TComponent>(RenderFragment)
methods toTestContext
, as well as various overloads to theMarkupMatches
methods, that also takes aRenderFragment
as the expected value.The difference between the generic
Render
method and the non-generic one is that the generic returns anIRenderedComponent<TComponent>
, whereas the non-generic one returns aIRenderedFragment
.Calling
Render<TComponent>(RenderFragent)
is equivalent to callingRender(RenderFragment).FindComponent<TComponent>()
, e.g. it returns the first component in the render tree of typeTComponent
. This is different from theRenderComponent<TComponent>()
method, whereTComponent
is the root component of the render tree.The main usecase for these are when writing tests inside .razor files. Here the inline syntax for declaring render fragments make these methods very useful.
For example, to tests the
<Counter>
page/component that is part of new Blazor apps, do the following (inside aCounterTest.razor
file):@code { [Fact] public void Counter_Increments_When_Button_Is_Clicked() { using var ctx = new TestContext(); var cut = ctx.Render(@<Counter />); cut.Find("button").Click(); cut.Find("p").MarkupMatches(@<p>Current count: 1</p>); } }
Note: This example uses xUnit, but NUnit or MSTest works equally well.
In addition to the new
Render
methods, a emptyBuildRenderTree
method has been added to theTestContext
type. This makes it possible to inherit from theTestContext
type in test components, removing the need for newing up theTestContext
in each test.This means the test component above ends up looking like this:
@inherts TestContext @code { [Fact] public void Counter_Increments_When_Button_Is_Clicked() { var cut = Render(@<Counter />); cut.Find("button").Click(); cut.Find("p").MarkupMatches(@<p>Current count: 1</p>); } }
Tip: If you have multiple test components in the same folder, you can add a
_Imports.razor
file inside it and add the@inherits TestContext
statement in that, removing the need to add it to every test component. -
Added support for
IJSRuntime.InvokeAsync<IJSObjectReference>(...)
calls from components. There is now a new setup helper methods for configuring how invocations towards JS modules should be handled. This is done with the variousSetupModule
methods available on theBunitJSInterop
type available through theTestContext.JSInterop
property. For example, to set up a module for handling calls tofoo.js
, do the following:using var ctx = new TestContext(); var moduleJsInterop = ctx.JSInterop.SetupModule("foo.js");
The returned
moduleJsInterop
is aBunitJSInterop
type, which means all the normalSetup<TResult>
andSetupVoid
methods can be used to configure it to handle calls to the module from a component. For example, to configure a handler for a call tohello
in thefoo.js
module, do the following:moduleJsInterop.SetupVoid("hello");
-
Added support for registering services in bUnits
Services
collection that implementsIAsyncDisposable
. Suggested by @jmaillet in #249.
Changed
List of changes in existing functionality.
-
bUnit's mock IJSRuntime has been moved to an "always on" state by default, in strict mode, and is now available through
TestContext
'sJSInterop
property. This makes it possible for first party Blazor components like the<Virtualize>
component, which depend on JSInterop, to "just work" in tests.Compatible with previous releases: To get the same effect as calling
Services.AddMockJSRuntime()
in beta-11, which used to add the mock IJSRuntime in "loose" mode, you now just need to change the mode of the already on JSInterop, i.e.ctx.JSInterop.Mode = JSRuntimeMode.Loose
.Inspect registered handlers: Since the new design allows registering invoke handlers in the context of the
TestContext
, you might need to get already registered handlers in your individual tests. This can be done with theTryGetInvokeHandler()
method, that will return handler that can handle the parameters passed to it. E.g. to get a handler for aIJSRuntime.InvokaAsync<string>("getValue")
, callctx.JSInterop.TryGetInvokeHandler<string>("getValue")
.Learn more issue #237. By @egil in #247.
-
The
Setup<TResult>(string identifier, Func<IReadOnlyList<object?>, bool> argumentsMatcher)
andSetupVoid(string identifier, Func<IReadOnlyList<object?>, bool> argumentsMatcher)
methods in bUnits JSInterop/MockJSRuntime has a new second parameter, anInvocationMatcher
.The
InvocationMatcher
type is a delegate that receives aJSRuntimeInvoation
and returns true. TheJSRuntimeInvoation
type contains the arguments of the invocation and the identifier for the invocation. This means old code using theSetup
andSetupVoid
methods should be updated to use the arguments list inJSRuntimeInvoation
, e.g., change the following call:ctx.JSInterop.Setup<string>("foo", args => args.Count == 2)
to this:
ctx.JSInterop.Setup<string>("foo", invocation => invocation.Arguments.Count == 2)
. -
Changed
AddTestAuthorization
such that it works in Razor-based test contexts, i.e. on theFixture
andSnapshotTest
types.
Removed
List of now removed features.
- A few bUnit internal xUnit assert helper methods, the custom
ShouldAllBe
methods, has mistakingly been part of the bunit.xunit package. These have been removed.
Fixed
List of any bug fixes.
-
When an
Add
call to the component parameter collection builder was used to select a parameter that was inherited from a base component, the builder incorrectly reported the selected property/parameter as missing on the type. Reported by @nickmuller in #250. -
When an element, found in the DOM tree using the
Find()
, method was removed because of an event handler trigger on it, e.g. ancut.Find("button").Click()
event trigger method, anElementNotFoundException
was thrown. Reported by @nickmuller in #251. -
In the built-in fake authentication system in bUnit, roles and claims were not available in components through the a cascading parameter of type
Task<AuthenticationState>
. Reported by @AFAde in #253 and fixed in #291 by @egil.