dotnet / MobileBlazorBindings

Experimental Mobile Blazor Bindings - Build native and hybrid mobile apps with Blazor
MIT License
1.2k stars 170 forks source link

A Unit Test Framework for MobileBlazorBindings #42

Open Kahbazi opened 4 years ago

Kahbazi commented 4 years ago

I'm very excited to see Blazor and Xamarin.Forms together. I hope this project pass its experimental mode soon so we could use it in production.

I worked with Razor Components Testing Library a little which gives us ability to write unit tests for Blazor components. I was thinking if it's possible to do the same concept for Xamarin.Forms.

I wrote some code as a proof of concept here (MobileBlazorBindings.Tests) and I think it works. We can write some unit tests for Blazor Mobile Components.

This is what a component and a unit test looks like:

<ContentPage>
    <StackLayout>
        <Button Text="Increment" OnClick="IncrementCount" />
        <Label Text="@("The button was clicked " + count + " times")" />
    </StackLayout>
</ContentPage>

@code
{
    int count;
    void IncrementCount()
    {
        count++;
    }
}
var page = await TestContext.Start<Counter>();

var label = page.FindFirst<Label>();
var button = page.FindFirst<Button>();

Assert.Equal("The button was clicked 0 times", label.Text);

button.Click();

Assert.Equal("The button was clicked 1 times", label.Text);

button.Click();
button.Click();

Assert.Equal("The button was clicked 3 times", label.Text);

Now I want to expand this into a full feature library, but since I'm not too much familiar on how Xamarin Forms internally works, before I start I wanted to ask for guidance to make sure that I'm on the right path. So I would appreciate it anyone could take a moment and checkout the code (MobileBlazorBindings.Tests) and give me a hint. @Eilon @SteveSandersonMS @jamesmontemagno @egil

Eilon commented 4 years ago

This sounds super interesting, I will definitely take a look! Did you have specific questions about how Xamarin.Forms works?

Kahbazi commented 4 years ago

To make the testing work I brute force my way to implement Application, DeviceInfo and IPlatformServices! I want to make sure I implement them right!

Also I think it would be great if I can Find Elements by AutomationId. Is it possible in Xamarin.Forms? If so I think AutomationId should be added to VisualElement.

I also want to know how Element.FindByName() works. How can I set a name on an Element so I can select them.

egil commented 4 years ago

Hi @Kahbazi, great work!

I too have been thinking about how to test the razor mobile components.

That said, I have not had the time to look at the mobile bindings yet, but my guess is that the razor component model is still the same, i.e. it is still a Renderer that passes through all components and renders what is in BuildRenderTree methods, using a RenderTreeBuilder, into XAML. In this case, that the MobileBlazorBindingsRenderer instead of the HtmlRenderer.

@Elion, is that more or less what is going on?

If this is the case, then it should be quite doable to add support for mobile components.

The big difference will be users will be working with and asserting against XAML/XML instead of HTML5. Other than that, many of the same techniques should still apply.

@Elion, the generated XAML, how big of a disconnect is there between the Razor markup devs are writing and the rendered XAML?

I ask because a Blazor dev without any Xamarin experience might find it hard to work with the rendered XAML as they don't see it in there day-to-day development. Compared to the web version of Blazor, where devs are writing HTML, and it is also what is being rendered out, there is very little disconnect between the rendered output and the source razor. That makes asserting against the rendered output much more natural. Ø

@Kahbazi, if you are interested, I would love to join forces on this.

Eilon commented 4 years ago

No XAML at all here. It renders components the usual way with all the RenderTree stuff. Then there are these Adapter things that instantiate the Xamarin.Forms controls directly.

egil commented 4 years ago

Ahh I see, so the result of a render is actually instantiated Xamarin.Forms controls, and when a rerender happens, these instances are updated?

Kahbazi commented 4 years ago

@egil I can absolutely use some help to get this project started.

when a rerender happens, these instances are updated?

I think so, based on how my unit test works. I get a label element and after each button click, the Text property of the same label instance got updated.

Eilon commented 4 years ago

Yes instances of the live controls are reused. Blazor does the diff, then Mobile Blazor Binding processes the diff by walking the live control tree and doing the needful (creating new controls, deleting controls, setting properties, attaching events, etc.).

egil commented 4 years ago

Ok. So that means that devs will have to be familiar with Xamarin.Forms' API to properly test the rendered/instantiated forms controls. That might be a turn off to some. But it also makes sense since they will test against the end result.

Do you see any good alternatives that keeps devs closer to the Blazor world?

Kahbazi commented 4 years ago

Do you see any good alternatives that keeps devs closer to the Blazor world?

I think whoever is using MobileBlazorBindings should become familiar with Xamarin.Forms. After all they need to use Xamarin.Essentials sooner or late.

Also I think most of the Blazor mobile component are mapped to a single element of Xamarin.Forms so we could have some extension methods to map back the Xamarin.Forms element to Blazor component for testing.

Eilon commented 4 years ago

Do you see any good alternatives that keeps devs closer to the Blazor world?

@egil the Blazor world is independent of what it renders to. I think I saw in your Blazor-for-the-web testing demo the developer has to 'know' the HTML. So in Mobile Blazor Bindings it's based on Xamarin.Forms (but not XAML), so you need to understand how such an app is structured, how the components behave, etc.

Do you have further thoughts on what "closer" might look like to you?

egil commented 4 years ago

@egil the Blazor world is independent of what it renders to. I think I saw in your Blazor-for-the-web testing demo the developer has to 'know' the HTML.

Absolutely, but they are also essentially writing HTML5 in their Razor components. So the Blazor world and the rendered world is quite close I think.

So in Mobile Blazor Bindings it's based on Xamarin.Forms (but not XAML), so you need to understand how such an app is structured, how the components behave, etc.

I do think it is completely fair to expect devs to understand the Xamarin.Forms architecture and how to structure apps.

However, and it might just be my expectations that are wrong here (and I have only watched your presentation, so i'm very uneducated on this), but I was left with the impression that if I understand the Blazor component model, life-cycle, bindings, etc., then I would be able to build mobile apps. Obviously, I cannot use my <div> HTML elements, but instead have to use the <StackLayout> component, etc.

I am not trying to be an ass, just to understand, but if I have to learn the details of Xamarin.Forms to build something, what is the benefit of using the mobile bindings over Xamarin.Forms?

Do you have further thoughts on what "closer" might look like to you?

As you have probably gathered, I am not at all familiar with Xamarin.Forms to really say for certain, and I am not sure it makes sense at all.

Kahbazi commented 4 years ago

what is the benefit of using the mobile bindings over Xamarin.Forms?

If by Xamarin.Form you mean Xaml, for me it is much easier to implement an app with mobile binding rather than Xaml.

egil commented 4 years ago

what is the benefit of using the mobile bindings over Xamarin.Forms?

If by Xamarin.Form you mean Xaml, for me it is much easier to implement an app with mobile binding rather than Xaml.

So that makes sense. Mobile bindings are much more like HTML than XAML, I guess.

egil commented 4 years ago

@Eilon , so a little sleep does wonders. I get what you are saying, that users have to know the DOM api when testing in web blazor, so in that way, the two scenarios match.

That said, if we look past the rendered output, I'm guessing there are quite a few similarities between the two, so a common testing core is probably realistic.

How relevant is it to testing to be able to specify the (emulated) environment, e.g. screen size/device capabilities, internet connection on/off, etc.?

Kahbazi commented 4 years ago

How relevant is it to testing to be able to specify the (emulated) environment, e.g. screen size/device capabilities, internet connection on/off, etc.?

I believe all those features could be available by adding a mock object to service collection.

I'm guessing there are quite a few similarities between the two, so a common testing core is probably realistic.

Which two are you talking about? Blazor web and Blazor mobile? I think the only similarities for these two is the component life cycle process, so maybe we could find a similar way to detect the tests and pass them to their own renderer, but other than that the rendered output is completely different.

However the output of Blazor Mobile Component and Xaml is Xamarin.Forms. So maybe both of these frameworks could be tested with a single testing framework which aims Xamarin.Forms. Right now I'm working on how to apply the same concept for Xamarin.Forms Xaml.

egil commented 4 years ago

Which two are you talking about? Blazor web and Blazor mobile? I think the only similarities for these two is the component life cycle process, so maybe we could find a similar way to detect the tests and pass them to their own renderer, but other than that the rendered output is completely different.

I think there are more similarities, such as providing input to Blazor components, but I am not sure.

However the output of Blazor Mobile Component and Xaml is Xamarin.Forms. So maybe both of these frameworks could be tested with a single testing framework which aims Xamarin.Forms. Right now I'm working on how to apply the same concept for Xamarin.Forms Xaml.

Sounds reasonable. If you are going to be working with XAML (XML) and performing comparisons of it, check out https://www.xmlunit.org/. I was using it before in my testing library, but obviously it didn't work great with HTML5, so I built AngleSharp Diffing instead. But I bet it will have no trouble with XAML, since it is legal XML, as far as I know.

Kahbazi commented 4 years ago

Thanks for the suggestion but for the approach that I have in mind I won't need to compare XAML, because at the end both Xamarin.Forms XAML and Blazor Mobile are rendered as Xamarin.Forms.Element and I want to create a test framework to do the assertion on Xamarin.Forms.Element.

Xamarin.Forms.Element is the rendered output of Xamarin.Forms XAML and Blazor Mobile, as HTML is the rendered output of Blazor web.

Eilon commented 4 years ago

Great discussion everyone, super excited to see this!

I think as far as what people want to test, it's not too different between XAML + Xamarin.Forms and Blazor + Xamarin.Forms. Though perhaps there's a bit more to test in the Blazor world because you are encouraged to put a bit more code/logic in the .razor file compared to a XAML file.

Kahbazi commented 4 years ago

Though perhaps there's a bit more to test in the Blazor world because you are encouraged to put a bit more code/logic in the .razor file compared to a XAML file.

I think if we include the view model that is bound to XAML in test framework, the tests could be useful for XAML as much as it is for Blazor.

joshmccall221 commented 4 years ago

Yes instances of the live controls are reused. Blazor does the diff, then Mobile Blazor Binding processes the diff by walking the live control tree and doing the needful (creating new controls, deleting controls, setting properties, attaching events, etc.).

This is beautiful!