Using a mocking tool to write the unit tests required for your existing C# app is an easy task with JustMock.
StackOverflow is probably the largest and best-known developer community, right? And here is a random question with 23K views (and it is not the only one regarding this delicate subject):
Can unit testing be successfully added into an existing production project?
If so, how and is it worth it?
Long story short—yes and it is totally worth it. Regarding the ‘how’—take your time and read on.
If you happen to be in the exact same position and are asking yourself the above question—welcome to the right place! No worries, there is a cure. And you do not even have to climb too steep of a hill for it.
I have already chosen my ‘victim’ for our little experiment. It is called ERP and is part of Telerik UI for WPF’s Sample Applications. You can check it out here and download its source code by following the steps from the Download Product Files article. Long story short, you need a Telerik account and the zip before the last from the following screenshot:
I am glad that we cleared the above up, we can go on with the real deal now. Ready? I'm ready.
Writing a thorough set of unit tests is definitely time-consuming. Even a simple if statement with one line of code and a Boolean condition would require at least two tests to cover the possible outcomes (one for true, and one for false). Imagine what would be if the lines of code and the conditions were more than one. Yes, it would all escalate pretty fast. 😂
Have you heard about mocking?
Mocking is a process employed in unit testing to simulate external dependencies.
Kurzgesagt (a.k.a. in English, in a nutshell), mocking is a concept in unit testing where real objects are substituted with fake objects that imitate the behavior of the real ones. It is done so that a test can focus on the code being tested and not on the behavior or state of external dependencies.
Of course, these fake (or mock) objects can be created and maintained manually. Doing it all by yourself could cost you tons of time and be ultimately unproductive (and sometimes boring 🤫). Oh, and let's not forget the case in which your app code is so complex that you need to either do a major refactoring, or (even worse)—rewrite it completely from scratch.
That's why I would suggest use a mocking framework. Especially when talking about apps written without the slightest idea for covering with unit tests.
What is a mocking framework?
Mocking frameworks are used to generate replacement objects like Stubs and Mocks. Mocking frameworks complement unit testing frameworks by isolating dependencies but are not substitutes for unit testing frameworks. By isolating the dependencies, they help the unit testing process and aid developers in writing more focused and concise unit tests. The tests also perform faster by truly isolating the system under test.
Currently, there are some free mocking frameworks on the market, yet my honest opinion is that they are simply insufficient. Most mocking frameworks have limitations as to what they can mock. The limitation comes from the way the mock object is created, which is through inheritance. The mock object can inherit only public APIs like virtual methods, abstract classes and interfaces. This functionality may be enough for your project as many companies have internal projects where all APIs can be public as they won’t be consumed by a third party. Yet, if your development team is dealing with internal, private logic, static calls or a dependency on a third-party library, then Telerik JustMock is a better suited solution. It helps you advance the unit testing of C# devs.
Spending money for a complete mocking tool like JustMock enables developers to focus solely on testing the system under test and forget about the distracting mocking details. Mock objects are created automatically in memory when the tests are run based on the simple configuration in the unit test. There are no ‘physical’ mock objects that must be maintained as the project changes.
Telerik JustMock is the fastest, most flexible, and complete mocking solution for crafting unit tests that support Blazor, ASP.NET Core, ASP.NET MVC, ASP.NET Web Forms, .NET Core, WPF, WinForms and the current preview of .NET 5. And it will definitely help you solve all potential problems before being busted by the QA. 🤓 Oh, yeah!
With Telerik JustMock, any developer could start creating mocks, subs and fakes and use them in their tests. Wanna learn how JustMock makes unit tests more concise? Okay, let's dive right in.
Let's first sum up the requirements of our experiment. We have:
I suggest we take a look at the ERP app for starters:
The times I have seen or interacted with it can be counted on the fingers of a hand. But that should not bother me, or you (well, at least not a lot).
Forgot to mention something about JustMock – it follows the Arrange-Act-Assert Pattern. The AAA Pattern makes a test look simpler and more structured by dividing it in three subsections, as explained below:
Assert – the section where all verifications of expectations and results are performed
Having the JustMock extension installed, you are guaranteed a flying start. The project templates that come with it helped me create a test project in seconds.
Now, I'm ready for unit testing!
See the edit and delete buttons at the left below the Breadcrumb bar? I bet there is a Command related to both.
Noticed that the Orders item is initially selected? Surprisingly (or not) its DataContext is the OrdersViewModel. Back to the commands—yup, there they are:
this
.EditRecordCommand =
new
DelegateCommand(
(o) =>
{
dialogFactory.TargetItemName =
this
.ItemName;
dialogFactory.ShowEditDialog(
this
.ItemToEdit);
this
.Refresh();
},
(o) =>
this
.ItemToEdit !=
null
);
this
.DeleteRecordCommand =
new
DelegateCommand(
(o) =>
{
dialogFactory.TargetItemName =
this
.ItemName;
if
(dialogFactory.ShowDeleteDialog())
{
(
this
.ItemToEdit
as
ISavableObject).Delete();
this
.Refresh();
}
},
(o) =>
this
.ItemToEdit !=
null
);
Let us see how the above can be covered with unit tests and write our first ones with Just Mock! The purpose of both tests—suppose there is a direct binding between the buttons and the commands. This way, we will be able to test the commands themselves. All dependencies (e.g., the ERPDialogFactory, the SalesOrderHeader, the CollectionView) will be mocked and the successful execution of the respective methods (e.g., the ShowEditDialog, ShowDeleteDialog, Refresh)—verified. Starting with the EditRecordCommand.
[TestMethod]
public
void
OrdersViewModel_EditRecordCommandExecutedSuccessfully()
{
//Arrange
var dialogFactory = Mock.Create<ERPDialogFactory>();
var salesOrderItem = Mock.Create<SalesOrderHeader>();
var collectionView = Mock.Create<QueryableDataServiceCollectionView<SalesOrderHeader>>();
Mock.Arrange(() =>
new
ERPDialogFactory()).Returns(dialogFactory);
Mock.ArrangeSet(() => dialogFactory.TargetItemName =
"myItemName"
);
Mock.Arrange(() => dialogFactory.ShowEditDialog(salesOrderItem)).DoNothing();
Mock.Arrange(() => collectionView.HasChanges).Returns(
true
);
Mock.Arrange(() => collectionView.RejectChanges()).InOrder();
Mock.Arrange(() => collectionView.Refresh()).InOrder();
//Act
var viewModel =
new
OrdersViewModel();
viewModel.SelectedItem = salesOrderItem;
Mock.Arrange(() => viewModel.ItemName).Returns(
"myItemName"
);
Mock.Arrange(() => viewModel.Items).Returns(collectionView);
viewModel.EditRecordCommand.Execute(
null
);
//Assert
Mock.Assert(dialogFactory);
Mock.AssertSet(() => dialogFactory.TargetItemName =
"myItemName"
, Occurs.Once());
Mock.Assert(salesOrderItem);
Mock.Assert(collectionView);
}
Covering the DeleteRecordCommand with a unit test looks a lot like the EditRecordCommand's:
[TestMethod]
public
void
OrdersViewModel_DeleteRecordCommandExecutedSuccessfully()
{
//Arrange
var dialogFactory = Mock.Create<ERPDialogFactory>();
var salesOrderItem = Mock.Create<SalesOrderHeader>();
var collectionView = Mock.Create<QueryableDataServiceCollectionView<SalesOrderHeader>>();
Mock.Arrange(() =>
new
ERPDialogFactory()).Returns(dialogFactory);
Mock.ArrangeSet(() => dialogFactory.TargetItemName =
"myItemName"
);
Mock.Arrange(() => dialogFactory.ShowDeleteDialog()).Returns(
true
);
Mock.Arrange(() => salesOrderItem.Delete()).DoNothing();
Mock.Arrange(() => collectionView.HasChanges).Returns(
true
);
Mock.Arrange(() => collectionView.RejectChanges()).InOrder();
Mock.Arrange(() => collectionView.Refresh()).InOrder();
//Act
var viewModel =
new
OrdersViewModel();
viewModel.SelectedItem = salesOrderItem;
Mock.Arrange(() => viewModel.ItemName).Returns(
"myItemName"
);
Mock.Arrange(() => viewModel.Items).Returns(collectionView);
viewModel.DeleteRecordCommand.Execute(
null
);
//Assert
Mock.Assert(dialogFactory);
Mock.AssertSet(() => dialogFactory.TargetItemName =
"myItemName"
, Occurs.Once());
Mock.Assert(salesOrderItem);
Mock.Assert(collectionView);
}
What about the print button at the right below the Breadcrumb bar? That button:
<
telerik:RadButton
x:Name
=
"Print"
local:PrintExportHelper.PrintTarget
=
"{Binding ElementName=gridView}"
Style
=
"{StaticResource GridNavigationButtonStyle}"
Width
=
"63"
>
<!--
Button content and tooltip here
-->
</
telerik:RadButton
>
It has a PrintTarget set and I am guessing this is what I'm looking for. Let's peek at where this PrintTarget comes from and what it is supposed to do. This appears to be a dependency property, the setter of which will be called, and respectively—the PropertyChanged, when the above Binding is executed. This leads us to:
private
static
void
OnPrintTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var button = d
as
RadButton;
var gridView = e.NewValue
as
RadGridView;
if
(button !=
null
&& gridView !=
null
)
{
button.Command =
new
DelegateCommand(OnPrintCommandExecuted);
button.CommandParameter = gridView;
}
}
private
static
void
OnPrintCommandExecuted(
object
obj)
{
var gridView = obj
as
RadGridView;
if
(gridView !=
null
)
{
var spreadsheet =
new
RadSpreadsheet();
var window =
new
RadWindow() { Width = 0, Height = 0, Opacity = 0, Content = spreadsheet };
window.Show();
spreadsheet.Workbook = gridView.ExportToWorkbook();
spreadsheet.Print(
new
PrintWhatSettings(ExportWhat.ActiveSheet,
false
));
window.Close();
}
}
Let's start from the button's XAML definition this time and test whether the binding is correctly resolved and respectively—clicking this button does its magic.
[TestMethod]
public
void
PrintExportHelper_PrintButtonClickExecutesPrintCommand()
{
//Arrange
Mock.NonPublic.Arrange(
typeof
(PrintExportHelper),
"OnPrintCommandExecuted"
, Arg.Expr.AnyObject);
//Act
var printButton = Mock.Create<RadButton>();
var args = Mock.Create<DependencyPropertyChangedEventArgs>();
var gridView = Mock.Create<RadGridView>();
Mock.Arrange(() => args.NewValue).Returns(gridView);
PrivateAccessor.ForType(
typeof
(PrintExportHelper)).CallMethod(
"OnPrintTargetChanged"
, printButton, args);
printButton.Command.Execute(gridView);
//Assert
Mock.Assert(
typeof
(PrintExportHelper));
}
This unit test does not seem sufficiently stable enough. It only ensures that there is a print button and when it is clicked, the static method of the above helper class is called. But does it function properly, i.e., what exactly happens when the button's command is executed? Let us test:
[TestMethod]
public
void
PrintExportHelper_PrintCommandExecutionSucceeded()
{
//Arrange
var gridView = Mock.Create<RadGridView>();
Mock.NonPublic.Arrange(
typeof
(PrintExportHelper),
"OnPrintCommandExecuted"
, gridView);
var spreadsheet = Mock.Create<RadSpreadsheet>();
var workbook = Mock.Create<Workbook>();
var window = Mock.Create<RadWindow>();
Mock.Arrange(() => window.Show()).IgnoreInstance().InOrder();
Mock.Arrange(() => window.Close()).IgnoreInstance().InOrder();
Mock.Arrange(() => gridView.ExportToWorkbook());
Mock.Arrange(() => spreadsheet.Print(Arg.IsAny<PrintWhatSettings>(),
null
)).IgnoreInstance();
//Act
PrivateAccessor.ForType(
typeof
(PrintExportHelper)).CallMethod(
"OnPrintCommandExecuted"
, gridView);
//Assert
Mock.Assert(
typeof
(PrintExportHelper));
Mock.Assert(gridView);
Mock.Assert(window);
Mock.Assert(spreadsheet);
Mock.Assert(workbook);
}
Here, we need to arrange three more things (apart from those in the previous test)—the window, the spreadsheet and its workbook (these will do the magic of exporting the grid data into a spreadsheet's workbook).
The act we are performing is calling the OnPrintCommandExecuted method with the mocked grid view.
The assert phase ensures that all the above arrangements were made, this time including the ones regarding the grid, the spreadsheet and its workbook apart from the PrintExportHelper.
This is the happy path of the print functionality. What about passing an item, different than the expected grid view—e.g., an empty string, to the print command? Let us cover that with a test—assuming we passed a fake argument, the print command execution is expected to fail, right?
[TestMethod]
public
void
PrintExportHelper_PrintCommandExecutionFailed()
{
//Arrange
var gridView = Mock.Create<RadGridView>();
Mock.NonPublic.Arrange(
typeof
(PrintExportHelper),
"OnPrintCommandExecuted"
, gridView).OccursNever();
var spreadsheet = Mock.Create<RadSpreadsheet>();
var workbook = Mock.Create<Workbook>();
var window = Mock.Create<RadWindow>(Constructor.NotMocked);
Mock.Arrange(() => window.Show()).IgnoreInstance().InOrder().OccursNever();
Mock.Arrange(() => window.Close()).IgnoreInstance().InOrder().OccursNever();
Mock.Arrange(() => gridView.ExportToWorkbook()).OccursNever();
Mock.Arrange(() => spreadsheet.Print(Arg.IsAny<PrintWhatSettings>(),
null
)).IgnoreInstance().OccursNever();
//Act
PrivateAccessor.ForType(
typeof
(PrintExportHelper)).CallMethod(
"OnPrintCommandExecuted"
, String.Empty);
//Assert
Mock.Assert(
typeof
(PrintExportHelper));
Mock.Assert(gridView);
Mock.Assert(window);
Mock.Assert(spreadsheet);
Mock.Assert(workbook);
}
Guess what, it did. Not the test. The command execution—we arranged that no invocations of the above methods have ever occurred when acting with an empty string instead of the expected grid view.
Well, seems like the export, delete and print functionalities work as expected. Good job!
What about the last button of this button bar—the export? Well, I'm leaving this as your JustMock homework. 📚
If you are intrigued (hopefully you are 🤞), I'll be more than happy to hear your honest feedback in the comments.
Whether you:
Regardless of the above ‘cases,’ don't be shy to:
Try The Latest JustMock Now
You won't regret it.
Viktoria is a Senior Front-end Developer with 5+ years of experience designing and building modern UI for desktop and mobile platforms on Microsoft/Telerik technology stacks. Her spare time is dedicated to friends and family and discovering new pieces of the world. Find her on Twitter and LinkedIn.