Update: Due to new personal commitments and more work commitments in 2021, I wasn’t able to make much progress with my weekly C# A-Z series on dev.to/shahedc. For now, I’ll focus on some new content for my regular blog (this blog, WakeUpAndCode.com) and hope to revisit the A-Z series with .NET 6.
Original Post:
I published my first ASP .NET Core A-Z series on WakeUpAndCode.com back in 2019, from January to June 2019. I followed this with a new A-Z series in 2020, simultaneously mirroring the posts on dev.to as well.
Going forward, my next A-Z series will cover 26 topics covering various C# language features. The C# A-Z series will be featured exclusively on my dev.to site under the .NET org:
Meanwhile, this site (WakeUpAndCode.com) will continue to feature new ASP .NET Core content based on .NET 5, Blazor and more! To get a sneak peak of what’s to come, check out my guest appearance on the .NET Docs Show (livestreamed Dec 7, 2020). You may jump ahead to 58:05 in the video for the sneak peek:
The above video teases my upcoming cinematic visualizer app, which will allow the end user to connect the dots within a cinematic universe, e.g. the Marvel Cinematic Universe. The source code will allow any .NET developer to learn more about C# and .NET 5, ASP .NET Core, Entity Framework, Azure App Service, Bot Framework, Azure Functions, and more!
The goal of the web app is to make use of all 3 project styles available in ASP .NET Core:
MVC (Model View Controller)
Razor Pages
Blazor
Developers frequently ask the developer community (and Microsoft) whether a particular web project type is preferred over the other. Last year’s blog series built upon the NetLearner web app by duplicating identical functionality across all three project types. This year, the cinematic visualizer app will attempt to use each project type of something specific.
MVC for data entry
Razor Pages for the Portal site
Blazor for the highly interactive portion
The above choices aren’t necessarily prescriptive for the type of web apps they will demonstrate. However, they should provide a starting point when developing ASP .NET Core web applications.
As promised, below is the initial release of the ASP .NET Core 3.1 A-Z ebook. This combines the 26 blog posts from the series of ASP .NET Core articles on this website.
You can find the complete ebook on GitHub using one of the links below:
The cover image was generated using the Canva mobile app
The 2020 eBook is still a work in progress 🙂
I’m using my Worker Service sample to auto-generate Word documents from each blog post by converting each article’s HTML into Word format using MariGold.OpenXHTML
After some tweaking, images have been manually resized using a Macro in the Word document. Automatic resizing doesn’t seem to work between HTML to Word conversions, but feel free to submit a Pull Request if you have suggestions on how to fix it.
Animated GIF images don’t work in the ebook, so a link to each original source has been included where they appear in a few chapters.
This is the twenty-first of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.
Whether you’re practicing TDD (Test-Driven Development) or writing your tests after your application code, there’s no doubt that unit testing is essential for web application development. When it’s time to pick a testing framework, there are multiple alternatives such as xUnit.net, NUnit and MSTest. This article will focus on xUnit.net because of its popularity (and similarity to its alternatives) when compared to the other testing frameworks.
In a nutshell: a unit test is code you can write to test your application code. Your web application will not have any knowledge of your test project, but your test project will need to have a dependency of the app project that it’s testing.
Here are some poll results, from asking 500+ developers about which testing framework they prefer, showing xUnit.net in the lead (from May 2019).
A similar poll on Facebook also showed xUnit.net leading ahead of other testing frameworks. If you need to see the equivalent attributes and assertions, check out the comparison table provided by xUnit.net:
The quickest way to set up unit testing for an ASP .NET Core web app project is to create a new test project using a template. This creates a cross-platform .NET Core project that includes one blank test. In Visual Studio 2019, search for “.net core test project” when creating a new project to identify test projects for MSTest, XUnit and NUnit. Select the XUnit project to follow along with the NetLearner samples.
The placeholder unit test class includes a blank test. Typically, you could create a test class for each application class being tested. The simplest unit test usually includes three distinct steps: Arrange, Act and Assert.
Arrange: Set up the any variables and objects necessary.
Act: Call the method being tested, passing any parameters needed
When you add a new xUnit test project, you should get a simple test class (UnitTest1) with an empty test method (Test1). This test class should be a public class and the test method should be decorated with a [Fact] attribute. The attribute indicates that this is a test method without any parameters, e.g. Test1().
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}
In the NetLearner Shared Library test project, you’ll see a test class (ResourceListServiceTests.cs) with a series of methods that take 1 or more parameters. Instead of a [Fact] attribute, each method has a [Theory] attribute. In addition to this primary attribute, each [Theory] attribute is followed by one of more [InlineData] attributes that have sample argument values for each method parameter.
[Theory(DisplayName = "Add New Resource List")]
[InlineData("RL1")]
public async void TestAdd(string expectedName)
{
...
}
In the code sample, each occurrence of [InlineData] should reflect the test method’s parameters, e.g.
[InlineData(“RL1”)] –> this implies that expectedName = “RL1”
NOTE: If you want to skip a method during your test runs, simply add a Skip parameter to your Fact or Theory with a text string for the “Reason”.
e.g.
[Fact(Skip=”this is broken”)]
[Theory(Skip=”we should skip this too”)]
Asserts and Exceptions
Back to the 3-step process, let’s explore the TestAdd() method and its method body.
public async void TestAdd(string expectedName)
{
var options = new DbContextOptionsBuilder<LibDbContext>()
.UseInMemoryDatabase(databaseName: "TestNewListDb").Options;
// Set up a context (connection to the "DB") for writing
using (var context = new LibDbContext(options))
{
// 1. Arrange
var rl = new ResourceList
{
Name = "RL1"
};
// 2. Act
var rls = new ResourceListService(context);
await rls.Add(rl);
}
using (var context = new LibDbContext(options))
{
var rls = new ResourceListService(context);
var result = await rls.Get();
// 3. Assert
Assert.NotEmpty(result);
Assert.Single(result);
Assert.NotEmpty(result.First().Name);
Assert.Equal(expectedName, result.First().Name);
}
}
During the Arrange step, we create a new instance of an object called ResourceList which will be used during the test.
During the Act step, we create a ResourceListService object to be tested, and then call its Add() method to pass along a string value that was assigned via InlineData.
During the Assert step, we compare the expectedName (passed by InlineData) with the returned result (obtained from a call to the Get method in the service being tested).
The Assert.Equal() method is a quick way to check whether an expected result is equal to a returned result. If they are equal, the test method will pass. Otherwise, the test will fail. There is also an Assert.True() method that can take in a boolean value, and will pass the test if the boolean value is true.
For a complete list of Assertions in xUnit.net, refer to the Assertions section of the aforementioned comparison table:
If an exception is expected, you can assert a thrown exception. In this case, the test passes if the exception occurs. Keep in mind that unit tests are for testing expected scenarios. You can only test for an exception if you know that it will occur, e.g.
Exception ex = Assert
.Throws<SpecificException>(() => someObject.MethodBeingTested(x, y));
The above code tests a method named MethodBeingTested() for someObject being tested. A SpecificException() is expected to occur when the parameter values x and y are passed in. In this case, the Act and Assert steps occur in the same statement.
NOTE: There are some differences in opinion whether or not to use InMemoryDatabase for unit testing. Here are some viewpoints from .NET experts Julie Lerman (popular Pluralsight author) and Nate Barbettini (author of the Little ASP .NET Core book):
Julie Lerman: “If you use it wisely and are benefiting (not as a replacement for integration tests) then they are AOK in my mind.”
Nate Barbettini: “I still might use InMemory for early rapid prototyping work, but no longer use it for testing. I don’t let any unit tests talk to the DB (pure classes and methods under test only), and no longer use InMemory for integration tests.”
To run your unit tests in Visual Studio, use the Test Explorer panel.
From the top menu, click Test | Windows | Test Explorer
In the Test Explorer panel, click Run All
Review the test status and summary
If any tests fail, inspect the code and fix as needed.
To run your unit tests with a CLI Command, run the following command in the test project folder:
> dotnet test
The results may look something like this:
As of xUnit version 2, tests can automatically run in parallel to save time. Test methods within a class are considered to be in the same implicit collection, and so will not be run in parallel. You can also define explicit collections using a [Collection] attribute to decorate each test class. Multiple test classes within the same collection will not be run in parallel.
For more information on collections, check out the official docs at:
NOTE: Visual Studio includes a Live Unit Testing feature that allows you to see the status of passing/failing tests as you’re typing your code. This feature is only available in the Enterprise Edition of Visual Studio.
Custom Names and Categories
You may have noticed a DisplayName parameter when defining the [Theory] attribute in the code samples. This parameter allows you to defined a friendly name for any test method (Fact or Theory) that can be displayed in the Test Explorer. For example:
[Theory(DisplayName = "Add New Learning Resource")]
Using the above attribute above the TestAdd() method will show the friendly name “Add New Learning Resource” in the Test Explorer panel during test runs.
Finally, consider the [Trait] attribute. This attribute can be use to categorize related test methods by assigning an arbitrary name/value pair for each defined “Trait”. For example (from LearningResource and ResourceList tests, respectively):
[Trait("Learning Resource Tests", "Adding LR")]
public void TestAdd() { ... }
[Trait("Resource List Tests", "Adding RL")]
public void TestAdd { ... }
Using the above attribute for the two TestAdd() methods will categorize the methods into their own named “category”, e.g. Learning Resource Tests and Resource List Tests. This makes it possible to filter just the test methods you want to see, e.g. Trait: “Adding RL”
Next Steps: Mocking, Integration Tests and More!
There is so much more to learn with unit testing. You could read several chapters or even an entire book on unit testing and related topics. To continue your learning in this area, consider the following:
MemberData: use the MemberData attribute to go beyond isolated test methods. This allows you to reuse test values for multiples methods in the test class.
ClassData: use the ClassData attribute to use your test data in multiple test classes. This allows you to specify a class that will pass a set of collections to your test method.
For more information on the above, check out this Nov 2017 post from Andrew Lock:
Mocking: use a mocking framework (e.g. Moq) to mock external dependencies that you shouldn’t need to test from your own code.
Integration Tests: use integration tests to go beyond isolated unit tests, to ensure that multiple components of your application are working correctly. This includes databases and file systems.
UI Tests: test your UI components using a tool such as Selenium WebDriver or IDE in the language of your choice, e.g. C#. For browser support, you may use Chrome or Firefox extensions, so this includes the new Chromium-based Edge browser.
While this article only goes into the shared library, the same concepts carry over into the testing of each individual web app project (MVC, Razor Pages and Blazor). Refer to the following documentation and blog content for each:
In order to set up a shared service object to be used by the controller/page/component being tested, Moq is used to mock the service. For more information on Moq, check out their official documentation on GitHub:
NOTE: Due to differences between bUnit beta 6 and 7, there are some differences between the Blazor guide and the NetLearner tests on Blazor. I started off with the Blazor guide, but made some notable changes.
Instead of starting with a Razor Class Library template for the test project, I started with the xUnit Test Project template.
There was no need to change the test project’s target framework from .NET Standard to .NET Core 3.1 manually, since the test project template was already Core 3.1 when created.
NetLearner is an ASP .NET Core web app to allow any user to consolidate multiple learning resources all under one umbrella. The codebase itself is a way for new/existing .NET developers to learn ASP .NET Core, while a deployed instance of NetLearner can be used as a curated link-sharing web application.
Registration for each web app has been disabled by default. To enable registration, please do the following:
Locate scaffolded Identity pages under /Areas/Identity/Pages/Account/
In Register.cshtml, update the page to include environments in addition to Development, if desired.
In Register.cshtml.cs, replace [Authorize] with [AllowAnonymous] to allow access to registration
Architecture
The current version of NetLearner on Github includes a shared .NET Standard Class Library, used by multiple web app projects. The web apps include:
MVC: familiar to most ASP .NET developers
Razor Pages: relatively new in ASP .NET
Blazor: the latest offering from ASP .NET
Future updates will also include:
Web API, exposed for use by other consumers
JavaScript front-end web app(s)
Note that an ASP .NET Core web app can contain various combinations of all of the above. However, the purpose of the NetLearner application code is to demonstrate how to achieve similar results using all of the above. So, each of the web app projects will be developed in parallel, using a shared library.
Shared Library
The shared library is a .NET Standard 2.1 library. This version was selected because .NET Core 3.x supports .NET Standard 2.1, as seen in the official documentation.
Database Context: for use by Entity Framework Core
Migrations: for EF Core to manage the (SQL Server) db
Models: db entities, used by web apps
Services: service classes to handle CRUD operations
Using terms from Clean Architecture references, you may think of the DB Context and Migrations as part of the Infrastructure code, while the Models and Services are part of the Core code. For simplicity, these are all kept in a single library. In your own application, you may split them up into separate assemblies named Core and Infrastructure.
Running the Application
In order to run the web app samples, clone the following repository:
In the Package Manager Console panel, change the Default Project to “NetLearner.SharedLib” to ensure that EF Core commands are run against the correct project
In the Package Manager console, run the Update-Database command
Verify that there are no errors upon database creation
To run the samples from Visual Studio 2019:
Run each web project one after another
Navigate to the links in the navigation menu, e.g. Lists and Resources
Add/Edit/Delete items in any of the web apps
Create one or more lists
Create one more resources, assign each to a list
Verify that your data changes are reflected no matter which web app you use
What’s Next?
In 2020, you can expect a new A-Z weekly blog series to cover 26 different topics from January through June 2020. To get a sneak peek of what’s to come, take a peek at the 2019 A-Z series.
Learn how you can build more robust web applications with automated unit testing! While there are plenty of resources for learning ASP.NET web application development, many developers are missing out on the knowledge and experience of implementing proper Unit Tests. As ASP.NET 5 gets ready for prime time, it’s essential for all .NET developers to get an understanding of how to build Unit Tests for real-world applications in a cloud-first mobile-first world.
The following presentation material was put together for a live audience for my in-person presentations.