Since the beginning of the year, I have been doing some research for a project related to time tracking. As part of the research, I build a tiny sample app to try out the technology stack that I’ll be using.
This week, I’m making my first few commits to the GitHub repository that I’ll be using for the real app. The app also now has a name, Time Tortoise, and a logo, the stern-looking creature at the top of this post.
A Review of the Technology Stack
Time Tortoise will be built with these technologies and patterns:
- Universal Windows Platform (UWP): An application architecture for building, among other things, modern Windows 10 apps.
- .NET Core and .NET Standard: Open-source frameworks that are compatible with UWP.
- C♯: A versatile .NET language.
- Extensible Application Markup Language (XAML): A markup language used for defining user interfaces.
- Model-View-ViewModel (MVVM): A pattern that separates user interface code from business layer code, so that the latter can be more effectively tested.
- Entity Framework Core (EF Core): An object-relational mapping (ORM) framework, a way to access a relational database using an object-oriented programming language like C♯.
- SQLite: An embedded database that is optimized for local single-user programs.
- xUnit.net: A unit testing framework for .NET.
- Moq: Described below.
In good open-source tradition, I’ll be adding to this list as needed. For the most current list, follow the Time Tortoise GitHub repository.
Solution Design
The code for UWP apps is generally organized in a Visual Studio solution, a collection of projects and related files. As I explained two weeks ago, a specific design is necessary to build the type of testable UWP MVVM app that I’m planning. My initial Time Tortoise solution design is based on the results of the experiments described in that post.
One change I made in the Time Tortoise solution compared to the example solution is to split the code into a few more projects. The model and data access layer now have their own projects, rather than being combined with the view model. This isn’t strictly necessary, but it’s cleaner to separate these components.
As of this week, TimeTortoise.sln contains five projects:
- TimeTortoise.UWP: A Universal Windows project for View code. The goal is to limit this project to user interface code that doesn’t need to be tested.
- TimeTortoise.ViewModel: A .NET Standard project for View Model code. The View Model massages application data into a form that is convenient for the View. View model classes can be tested as thorougly as necessary.
- TimeTortoise.Model: A .NET Standard Project for Model code. The Model can contain plain C# classes as well as business logic. Like the View Model, the Model is also designed to be testable.
- TimeTortoise.DAL: A .NET Standard Project containing the Data Access Layer, the Entity Framework code that communicates with SQLite to read from and write to the database.
- TimeTortoise.ViewModel.Tests: A modified .NET Core Class Library project, configured as specified in the xUnit.net documentation. This test project targets TimeTortoise.ViewModel.
Moq
I covered several unit testing topics over the last two weeks, but I have a few more to add before my unit testing toolbox is ready to go. The test topic for this week is Moq.
I explained last week how interface-based programming combined with test doubles is an effective test strategy. As an example, I created an interface for my Repository
class. Since Repository
is responsible for making database calls, having a Repository
test double allows us to run tests without using a database.
Last week, my example test double was a repository stub, a hand-coded implementation of IRepository
. That worked, but it’s not an ideal approach in the long run, since it would mean coding and maintaining one or more stub classes for every interface.
As an alternative, I’m going to be using mocks rather than stubs as my primary test double. To create my mock objects, I’m using a framework called Moq. Moq creates test doubles on the fly, based on an interface. This means the only code to write and maintain is the code in the unit test. And it’s easy to reconfigure the behavior of the test double as needed for each test.
Moq works with the .NET Core/xUnit.Net setup that I’m using, but only if you tell NuGet to include prerelease packages when downloading it. From the Package Manager Console, that’s as easy as running Install-Package Moq -Pre
. Eventually the .NET Core-compatible version of Moq may move out of prerelease.
Here’s my first simple test using Moq. The class under test is MainViewModel
, the view model that supports the MainPage
view. MainViewModel
needs a repository, which Moq can create from IRepository
.
// Arrange
var activities = new List<Activity>
{
new Activity {Name = "TestName1"},
new Activity {Name = "TestName2"}
};
var mockRepository = new Mock<IRepository>();
mockRepository.Setup(x => x.LoadActivities()).Returns(activities);
var mvm = new MainViewModel(mockRepository.Object);
// Act
mvm.LoadActivities();
// Assert
Assert.Equal(2, mvm.Activities.Count);
Assert.Equal("TestName1", mvm.Activities[0].Name);
Assert.Equal("TestName2", mvm.Activities[1].Name);
The test works as follows:
Arrange
- Create a list containing two activities, each having a different name.
- Using Moq, create a mock repository based on the
IRepository
interface. - Tell the mock repository that when we call
LoadActivities
, it should return the test list. This is a common Moq use case: rather than writing a stub repository that returns a fixed value, tell Moq to generate a repository on the fly with the desired behavior. - Initialize
MainViewModel
with the mock repository. Note thatmockRepository
has typeMock
, whilemockRepository.Object
has typeRepository
, which is what we need for our test.
Act
- Call
LoadActivities
on the view model. Since the view model was initialized with a mock repository, it doesn’t need a database to retrieve the activity list.
Assert
- Verify that the view model’s activity list has the activities we expect. This verifies that when the view model loads activities from the database, they are exposed in the property that the view will read from.
Using similar tests, we can exercise all of our application logic without depending on a database. Since these tests run fast, we can write a lot of them and run them frequently. A few integration tests using the SQLite in-memory database (as explained last week) suffice to verify that end-to-end scenarios work.
App Checklist
Now that I’m starting to code an actual project rather than an example app, I took care of a few initial tasks:
- Created a TimeTortoise repository on GitHub.
- Got a vector image from Shutterstock to use as a logo. I found UWP Visual Assets Generator useful for creating a set of visual assets in various sizes. The only problem I ran into was a “current image exceeds the maximum image file size” error for some of the larger assets, even after compressing them with PNGGauntlet. I’m ignoring that error for now, since it doesn’t seem to affect anything. (It’s probably a Windows App Store requirement).
- Reserved a domain, www.timetortoise.com, which now points to the GitHub Pages site for the project.
- Picked an open source license, Apache 2.0. I considered MIT as well, but ultimately went with Apache. Either one would have been fine. I found Which License Should I Use? MIT vs. Apache vs. GPL to be useful in deciding.
And that’s all for now.