---
title: "bUnit Generators - Generate your stubs automatically"
description: "We made a recent addition to [bUnit](https://bunit.dev) that we think will make your life easier. We call it **bUnit Generators**. It's a set of code generators that will help you create stubs for your third-party components."
updated: 2023-12-17
tags: ["Blazor", "bUnit"]
canonical: "https://steven-giesel.com/blogPost/76b40ab1-033b-488e-b11a-a5922fa8a4b7/bunit-generators-generate-your-stubs-automatically"
---

# bUnit Generators - Generate your stubs automatically

We made a recent addition to [bUnit](https://bunit.dev) that we think will make your life easier. We call it **bUnit Generators**. It's a set of code generators that will help you create stubs for your third-party components.

## The problem
When you write unit tests, you try to be as isolated as possible. You want to test your component in isolation, without any dependencies. But then the real world hits - and your components aren't in isolation at all. Let's face it: The majority of (enterprise) apps rely on third-party components. How likely is it to create a component library on your own?

Back to "isolation": Why do you want to isolate your component? Mainly because those third-party components are not under your control. They might change their API, they might have bugs, and they might not work as expected. You don't want to test their code; you want to test yours. (And you don't get paid/spend your resources on testing their code.)

So when we do this in Blazor and bUnit, the normal way is via stubbing components. Until now, you could do the following:
```csharp
[Fact]
public void MyTest()
{
    ComponentFactories.AddStub<ThirdPartyComponent>();
    // ...
}
```

This will add an empty stub for the `ThirdPartyComponent` to the test context. In many cases that is nice and you are done! But sometimes you want to assert or check what is passed into those components. And that's where it gets tricky. Here is an example of doing so. Imagine our `ThirdPartyComponent` has a `Counter` parameter that is of type `int` and we want to check that it is set to `42`:
```csharp
[Fact]
public void MyTest()
{
    ComponentFactories.AddStub<ThirdPartyComponent>();
    // ...

    var stub = cut.FindComponent<Stub<ThirdPartyComponent>>();
    stub.Instance.Parameters.First(s => s.Key == "Counter").Value.Should().Be(42);
}
```

That isn't very intuativ and also doesn't work if they change the public API. So we need a better way to do this.

## Meet: Source code generators
Before we go into some examples and code, let's talk about what, in the majority of cases, you want from a stub. The stub should mimic the original component as closely as possible. That means it should have the same public API. And that means in the majority of cases, you want to mimic the public parameters (including cascading ones). And that's what we do with the source code generators.

And we are offering with [bUnit.generators](https://www.nuget.org/packages/bunit.generators/): Generating stubs for you. And we are offering two ways:
1. Via the new interceptor feature introduced with .net 8
2. Via a new attribute

In both cases, you want to add the following package to your test project

```no-class
dotnet add package bunit.generators
```

### Interceptor
The interceptor is a new feature introduced with .net 8. It allows you to intercept calls to methods and properties. 
To make it work, we first have to enable the new interceptor feature. For this we have to adapt the `csproj` file of our test project. We have to add the following lines to the `PropertyGroup` section:

```xml
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <!-- ... -->
        <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Bunit</InterceptorsPreviewNamespaces>
    </PropertyGroup>
```

Now what the interceptor does is to intercept the call to `AddStub<TComponent>()` and generate the stub for you. So our test from above looks like this:
```csharp
[Fact]
public void MyTest()
{
    // ...
    ComponentFactories.AddStub<ThirdPartyComponent>();
    // ...

    var stub = cut.FindComponent<ThirdPartyComponentStub>();
    stub.Instance.Counter.Should().Be(42);
}
```

Two main differences:
1. We are using the `ThirdPartyComponentStub` instead of the `Stub` component - that is the component the generator created for us.
2. We have strongly typed stubs that mimic the public API of the original component.

The whole chain starts with the `AddStub` method that gets intercepted. The interceptor then generates the stub for us. And that's it. We can now use the strongly typed stubs in our tests.

You can check out the generated class - in our case, it looks like this:

```csharp
namespace MyNamespace;

internal partial class ThirdPartyComponentStub : global::Microsoft.AspNetCore.Components.ComponentBase
{
	[global::Microsoft.AspNetCore.Components.ParameterAttribute]
	public int Counter { get; set; }
}
```

The `partial` modifier allows us to extend the class with our own code. So we can add our own logic to the stubs. That is especially helpful if you want to mimic some other behavior like `public` methods.

As the interceptor is still in preview and needs some setup, we also introduced a new attribute that does partially the same:

### Attribute
The attribute is a marker to generate a stub. The test from above would look like this

```csharp
[Fact]
public void MyTest()
{
    // ...
    ComponentFactories.Add<ThirdPartyComponent, ThirdPartyComponentStub>();
    // ...

    var stub = cut.FindComponent<ThirdPartyComponentStub>();
    stub.Instance.Counter.Should().Be(42);
}


[ComponentStub<ThirdPartyComponent>)]
internal partial class ThirdPartyComponentStub;
```

The attribute is a marker that tells the generator to generate a stub for the `ThirdPartyComponent`. The generator will then generate a stub with the name `ThirdPartyComponentStub`. The `ComponentFactories.Add` method then adds the stub to the test context. The rest is the same as above.

## Conclusion

The features I showed are still "in preview". We are still working on them and will improve them over time. But we think they are already very helpful and will make your life easier. If you have any feedback, please let us know. Head over to the GitHub repository (https://github.com/bUnit-dev/bUnit) and let us know your thoughts!

## Resources
 * bUnit GitHub Repository: https://github.com/bunit-dev/bunit
 * bUnit Documentation: https://bunit.dev/