With our current test project, we have several classes that we created to “mock” some simple behavior for our tests: MockGameSession, MockJSRuntime, and MockIconProvider
. As we build out our game, we will need to mock more classes to help simplify our testing. Creating mock objects manually is repetitive and time consuming, so to increase our productivity, we will try automatic generation of mock objects by using a mocking framework.
A mocking framework is used to create replacement objects like Fakes, Stubs and Mocks. It is used to isolate each dependency and help developers perform unit testing in a concise, quick, and reliable way.
There are several different mock frameworks available for .NET. But for our project, we’re going use the Moq framework.
Installing Moq Framework
To install the Moq package, we need to use the NuGet Package Manager again. Launch it by right clicking on the SimpleRPG.Game.Tests project and select Manage NuGet Packages from the context menu.

Then Browse for the Moq package and click the Install button (accepting any license requests). This will install the Moq framework into our test project.
Remove MockJSRuntime & MockIconProvider
Both of these classes are mocked as part of registering Blazorise for component tests (in Lesson 2.4) in the TestServiceProviderExtensions.AddBlazoriseServices
extension method. These classes just implement a couple of interfaces and do nothing when their methods are called. This is the simplest form of mock objects.
We are going to delete these two classes, and then use Moq to create mock instances of these two interfaces. The new code in the TestServiceProviderExtensions.cs file will look like:
using Blazorise;
using Blazorise.Bootstrap;
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Moq;
namespace SimpleRPG.Game.Tests.Mocks
{
public static class TestServiceProviderExtensions
{
public static void AddBlazoriseServices(this TestServiceProvider services)
{
var mockJsRuntime = new Mock<IJSRuntime>().Object;
services.AddSingleton<IClassProvider>(new BootstrapClassProvider());
services.AddSingleton<IStyleProvider>(new BootstrapStyleProvider());
services.AddSingleton<IJSRunner>(new BootstrapJSRunner(mockJsRuntime));
services.AddSingleton<IJSRuntime>(mockJsRuntime);
services.AddSingleton<IComponentMapper>(new ComponentMapper());
services.AddSingleton<IThemeGenerator>(new BootstrapThemeGenerator());
services.AddSingleton<IIconProvider>(new Mock<IIconProvider>().Object);
services.AddSingleton<BlazoriseOptions>(new BlazoriseOptions());
}
}
}
The first thing we’ll notice is that this file has been greatly simplified. We deleted our mock implementation of IJSRuntime
and IIconProvider
. The remaining code is just the code that registers the Blaorise Bootstrap services.
Line #14 above creates a new instance of of a mock for the ISJRuntime
interface. The Mock
class (which is part of the Moq framework) is a generic class, so that we can define what type we are mocking. The implementation of this new mock class provides properties and methods defined by the interface which return default values and perform no operations on method calls… just like we did in our handwritten mock classes. The Mock<T>.Object
property casts our mock object to the type we used to define the generic definition.
Note that Moq works really well with interfaces and classes… we can use either in Mock<T>
definition. However when wanting to overwrite the implementation of methods/properties, we need to use interfaces or classes with virtual methods/properties that Moq will override. This does put design constraints on our implementation, but coding to interfaces is a good design practice anyway to help keep our components more loosely coupled.
Then, we use the mockJsRuntime variable in calls to register types for for IJSRunner
and IJSRuntime
.
Finally on line #22, we replace the use of our MockIconProvider
with another definition of the Moq type. This time Mock<IIconProvider>
gives us a simple mock implementation of the IIconProvider
interface.
Removing MockGameSession
Let’s look at a more complicated mocking scenario. To this point our mock objects have had no implementation. But what about when we need to mock a object that has some operation or data that needs to be returned and used in our calling code? We can still use the Moq framework to return the test data that we require, but with just a little more setup.
Defining IGameSession Interface
As we described earlier, coding to an interface is a good design pattern to follow when we want to hide the implementation and allow two loosely coupled components to work together via the interface definition. This is what we want for the GameSession
. It makes our code more robust and makes it easier to mock the view model.
First, we’ll define the IGameSession
interface by creating a new C# source file. We will define the public interface that we want to use to communicate between the MainScreen
page and the GameSession
view model.
using SimpleRPG.Game.Engine.Models;
namespace SimpleRPG.Game.Engine.ViewModels
{
public interface IGameSession
{
Player CurrentPlayer { get; }
void AddXP();
}
}
For now, our interface just has the CurrentPlayer
property and the AddXP
method.
Then, we need to use the interface in the definition of our GameSession
view model.
using SimpleRPG.Game.Engine.Models;
namespace SimpleRPG.Game.Engine.ViewModels
{
public class GameSession : IGameSession
{
public Player CurrentPlayer { get; private set; }
public GameSession()
{
this.CurrentPlayer = new Player
{
Name = "DarthPedro",
CharacterClass = "Fighter",
HitPoints = 10,
Gold = 1000,
ExperiencePoints = 0,
Level = 1
};
}
public void AddXP()
{
this.CurrentPlayer.ExperiencePoints += 10;
}
}
}
I’m not going to go into all of the details of using an interface, but notice that in line #5 where we define the GameSession
class, we say that it inherits from IGameSession
. This allows us to cast between the two types. And the GameSession
class must implement methods and properties that match the definitions in the IGameSession
interface.
Note: There is also a good description of using interfaces in the WPF version of this game sample.
References Using IGameSession
Now that we have the interface, we need to replace direct references to GameSession
with references to IGameSession
. This gives us the ability to replace the implementation of IGameSession
in our tests.
Therefore, change Program.ConfigureAppServices
to register the interface and the implementation class with the DI container.
private static void ConfigureAppServices(IServiceCollection services)
{
// add app-specific/custom services here...
services.AddSingleton<IGameSession, GameSession>();
}
Then, change the MainScreen
page to @inject
the interface rather than the concrete type.
@page "/"
@inject IGameSession ViewModel
<Row Style="height: 5vh; min-height: 32px">
<Column ColumnSize="ColumnSize.Is12" Style="background-color: aliceblue">
<Heading Size="HeadingSize.Is3">Simple RPG</Heading>
</Column>
</Row>
<Row Style="height: 60vh">
<Column ColumnSize="ColumnSize.Is3.OnWidescreen.Is12" Style="background-color: aquamarine">
<PlayerComponent Player="@ViewModel.CurrentPlayer" />
<Button Color="Color.Secondary" Outline="true" Clicked="@ViewModel.AddXP">Add XP</Button>
</Column>
<Column ColumnSize="ColumnSize.Is9.OnWidescreen.Is12" Style="background-color: beige">
Game Data
</Column>
</Row>
<Row Style="height: 30vh">
<Column ColumnSize="ColumnSize.Is3.OnWidescreen.Is12" Style="background-color: burlywood">
Inventory/Quest
</Column>
<Column ColumnSize="ColumnSize.Is9.OnWidescreen.Is12" Style="background-color: lavender">
Combat/Movement Controls
</Column>
</Row>
Update the MainScreen Test
Now that our code implements and uses the IGameSession
interface, we change our MainScreenTests
class to also use the interface as well.
First, we are going to delete the MockGameSession
class that we implemented earlier, because we’re going to replace it with a Mock<T>
implementation instead.
Then, we will update the MainScreenTests
class to the following:
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using SimpleRPG.Game.Engine.Models;
using SimpleRPG.Game.Engine.ViewModels;
using SimpleRPG.Game.Pages;
using SimpleRPG.Game.Tests.Mocks;
using Xunit;
namespace SimpleRPG.Game.Tests.Pages
{
public class MainScreenTests
{
private readonly Mock<IGameSession> session = new Mock<IGameSession>();
public MainScreenTests()
{
session.SetupGet(p => p.CurrentPlayer).Returns(
new Player
{
Name = "TestPlayer",
CharacterClass = "TestClass",
Level = 1,
HitPoints = 8,
});
}
[Fact]
public void SimpleRender()
{
// arrange
using var ctx = new TestContext();
ctx.Services.AddBlazoriseServices();
ctx.Services.AddSingleton<IGameSession>(session.Object);
// act
var cut = ctx.RenderComponent<MainScreen>();
// assert
var expected = @"<th scope=""col"" class="""" style="""" blazor:onclick=""2"" rowspan=""2"">";
Assert.Contains(expected, cut.Markup);
Assert.Contains("Player Data", cut.Markup);
Assert.Contains("TestPlayer", cut.Markup);
Assert.Contains("TestClass", cut.Markup);
}
}
}
In this class, we define a member variable for our IGameSession
mock, just like we did earlier in this lesson. Then in our test constructor, we configure some information in that mock object by calling Mock<T>.SetupGet
. SetupGet
allows up to change the behavior of the property getter for our IGameSession.CurrentPlayer
property. Whenever our test requests the CurrentPlayer
, we will return the instance of the test Player
that we create in this constructor. This allows us to customize the behavior and data provided by our mock object without having to create our own implementation of the class.
Finally, when we register our service with the bUnit TestContext
, we must register the interface (as we did in the Program
class) not the implementation class. The remainder of our test code and validation remains the same.
In conclusion, we have added a powerful tool in our testing capabilities. We can use the Moq framework to mock types from our game engine and also mock interfaces defined by Blazor and ASP.NET, like we did with IJSRuntime
. Being able to easily mock types that we don’t own (like system interfaces) allows us to build more isolated, predictable tests.