Replies: 2 comments 3 replies
-
Simplify core types and make them extensible insteadI do like to simplify those parts. One thing we could tackle and review is the abstraction levels we are currently having in bUnit.
The most probable type a user is facing is either If we take that further both interfaces ( |
Beta Was this translation helpful? Give feedback.
-
AngleSharpWrappersI really like the factory concept of AngleSharp. If we have the same objects updating "themselves" a lot of assertions might get easier overall without wrapping them in types. With that, we could even think of removing var cut = await RenderAsync<MyComp>();
var snapshot = new SnapshotComparer(cut); // or SnapshotComparer.Default.Get(cut);
...
snapshot.GetChangesSinceLastRender().HasSomeChanges(); If we also have a factory model here, the user can extent the behavior. For example, we could have a snapshot compare that ignores certain aspects (like MarkupMatches ignores certain things if told so). |
Beta Was this translation helpful? Give feedback.
-
bUnit version 2
I want to start a discussion of where we should take bUnit version 2, what the high-level goals are, and what the API changes we are planning should be.
High-level goals
Remove unnecessary abstractions
Some abstractions were put in place to support other things like MobileBlazorBindings. It still makes sense to have a core lib, i.e.
bunit.core
, since 3rd parties can take a dependency on that if they want to provide extensions to the core types likeITestContext
orIRenderedFragment
.Simplify core types and make them extensible instead
For example, remove support for
SaveSnapshot
,GetChangesSinceSnapshot
, andGetChangesSinceFirstRender
fromIRenderedFragment
and instead make the functionality possible through customIRenderedFragment
subtypes, that users can use if they want that functionality.Rename types and interfaces that overlap with over libraries and Blazor's types
For example: Rename
TestContext
toBunitContext
to avoid conflict with MSTest and NUnit'sTestContext
type. Another example is renamingTestRenderer
toBunitRenderer
.Renaming some test doubles, e.g.
FakeNavigationManager
toBunitNavigationManager
. That allows us to evolve and change bUnits navigation manager without breaking with specific test conventions.To align with Blazor's API, we could align with the concept of
RootComponents
and renameTestContext.RenderTree
toTestContext.RootComponents
.100% deterministic testing
This includes:
One renderer per (root) render
Currently, the
TestContext
creates one TestRenderer and uses that for all calls toTestContext.Render
method calls. Instead, theTestContext
should be considered a "configuration hub" where users can set up services, root components, and JSInterop, and then each root/initial render call gets a unique renderer.This will align more with how Blazor works, i.e. one renderer per "application", and will allow future scenarios more easily, such as enabling predictable "Shallow Render", e.g. a render that only renders the component at the top of the component tree.
The downside is additional overhead for tests that calls Render multiple times. However, the expectation is that almost all tests only perform one render call anyway, so that actual impact is likely going to be negligible.
Open question: Should the TestServiceProvider be shared?
Public API changes
Here are the current suggestions for changes to the public API.
If the list grows too large, we could consider providing a
bunit.compat
package that maps between the v1 and v2 APIs.AngleSharpWrappers
The purpose of the wrappers is to allow users to keep a reference to one element between renders, and if it changes, not having to query/find it again, but see the updates directly in the already referenced element.
Recent changes in the AngleSharp library enable us to achieve a similar result and allow us to add additional metadata to elements and perhaps nodes too. The key seems to be the introduction to factories that AngleSharp uses to create elements, and the ability to replace the factories.
With changes to
BunitHtmlParser
, we can potentially improve the user experience of the DOM node users are accessing. This could be through creating customBunitHtmlElement
types, that have additional functionality needed in bUnit tests, e.g.:IRenderedComponent
that created themBelow are some rough experiments with using
IElementFactory
:bunit.core
WIP!
The outline of a replacement for
TestContext
/TestContextBase
.The idea here is to simplify the render methods to three options, a basic one that takes the simplest form, a
RenderFragment
, and onethat exposes the render fragment/parameter builder version currently called RenderComponent. I do not want to carry over the other variants of RenderComponent, because I see very little usefulness in them and they were just kept around to avoid breaking changes.
I include both a generic and none-generic version of the Render method that takes a RenderFragment. It's not a great solution, but it also feels redundant for users that write tests in .razor files and want to just pass in
@<MyComp />
to the Render method to also have to specify the component name as a generic.Usage of BunitContext:
I would like to get away from having both
IRenderedFragment
andIRenderedComponent
. We actually never return anIRenderedComponent
, when we're just rendering a component without knowing its type, it's just aRenderedComponent<IComponent>
that is returned. I think I like better just to be explicit about it, and then users can inspect the result with their debugger and perhaps figure out that they can get theInstance
of their component.Instead of having
SetParametersAndRender
andRender
methods, I think it's just as good to just have aRenderAsync
methods that users can pass new parameters to via an optional parameter builder.The
OnAfterRender
methods are a bit inspired by the similarly named method onComponentBase
. The idea with these methods is that a user can pass in an action/callback, that is invoked every time the component completes a render cycle. So this is a very low-level version of the "wait for" methods we currently have, and the methods should serve as the base for the more high-level "wait for assertion"/"wait for element" methods, as well as custom logic such as saving snapshots/state between renders for advanced diffing/assertion scenarios.The
OnAfterRender
API idea is pretty fresh. Not sure if it could lead to memory leaks since there is no way to "unregister" the actions. Perhaps a different pattern is needed if this is a concern.Whenever a
IRenderedComponent<T>
is created, this factory should be used by theBunitRenderer
. It will allow 3rd parties to provide a customIRenderedComponent<T>
type, which they can add custom logic to, such as the SaveSnapshot/GetChangesSinceSnapshot feature in v1. That should also make it possible for us to move to an AngleSharprenderer version at some point.
bunit.web
TODO
bunit.web.testcomponents
Remove project and subtypes completely.
Beta Was this translation helpful? Give feedback.
All reactions