I blogged about this earlier in the year , but this article will expand on that by re-organizing to give information for my presentation at SD Code Camp 2018 .
I’ve shared my PowerPoint for SD Code Camp 2018 which has a lot of the same information.
It's official. See you NE Code in August. @amegala
— Kevin Logan (@alignedDev) May 3, 2019
I'm looking forward to it! pic.twitter.com/qgcZlzAANs
Here’s a slightly updated version for NE Code .
How would you like to do less manual testing, reduce fear: FDD and fear of deployment, stop fixing the same bugs over and over, deploy more frequently, force clarity of business rules, document assumptions, help others on board more quickly, increase business success, reduce stress, improve your life and the lives of your team (dev, ops, QA, managers, execs) and improve overall happiness?
Does that sound impossible and utopian? Maybe, but I’ve seen the promised land (at least parts of it). I have also been stuck wandering the wilderness of bug regressions, wasted time, energy and lost opportunities.
The journey to the promise land can be long (hopefully it won’t take you 40 years), is made up of many steps (crawl, walk, run) and it won’t be easy, but is worth the effort.
I am convinced that automated testing is one of the cornerstones (*) to successful software projects and DevOps. This is backed by research aka Accelerate and experience.
My goal is that by the end of the hour you’ll be motivated to increase learning with your team and start or continue adding more automated tests to verify your systems. I hope you’ll go back to work on Monday thinking “I can and should do this!”
Let’s talk more about automated testing.
(I added the intro for NE Code 2019 to spice it up. I originally started with my intro slide and the Goal section below).
That you’ll leave with an idea of how to get unit testing started in your current or upcoming projects. You’ll be able to go back to work on Monday, try unit testing and start learning, thinking “I can do this!”.
I hope to help you get started moving past not knowing how and where to start and make automated testing as normal to you as writing code.
The hope is that this will lead to higher quality code, more confidence in refactoring, easier on boarding for teammates, less stressful and more often releases and better developer lives.
My code is on Github for you to look at. There are notes in the README.md about the project.
Testing Pyramid => I’m focused on the unit testing part
Follow the Testing Pyramid .
See the more in-depth version of this .
using(var httpClient = new HttpClient()) {}
using (FileStream fs = File.OpenRead(path))
I created some tests in preparation of doing a live coding session in my presentation. Here is my “script” that I’ll be following. I’ll start in the presentationStart branch. I’ve added snippets of code that I removed for reference as I’m coding.
I’m following a TDD approach with showing the Red > Green > refactoring loop. See Kent Beck’s Test Driven Development for a more in-depth example.
Microsoft docs on Testing MVC Controllers
Update in August, 2019, I’ve since given a few workshops since my first presentation. Instead of live coding, I’ll be using the workshop branch and doing more un-commenting of code. This is more straightforward and less stressful. I’m leaving the steps below, so you can see my thought process as I’d approach this in a TDD manner.
Given an API call
When asking for current temp and no zip code is given
Then returns a 400
// Arrange
// Act
// Assert
Assert.Inconclusive();
var result = await controller.CurrentTemp(0);
return Task.FromResult(52.5);
to get it to buildAssert.AreEqual(52.5, result);
Assert.AreEqual(400, (result.Result as BadRequestObjectResult).StatusCode);
public async Task<ActionResult<double>> CurrentTemp(int zipCode)
return Ok(10);
Given an API call
When asking for current temp
Then it calls the weather Api with the correct zip code
Assert.IsTrue(false);
=> failIGetWeatherHttpClient
and HttpClientFactory?ApixuClient
services.AddHttpClient<IGetWeatherHttpClient, ApixuClient>();
to Startup
var fakeTemp = 72.6; getWeatherHttpClient.Setup(wp => wp.GetCurrentTempAsync(zipCode)).ReturnsAsync(fakeTemp);
var response = await weatherController.CurrentTemp(zipCode);
getWeatherHttpClient.Verify(w => w.GetCurrentTempAsync(zipCode), Times.Once);
var result = await weatherHttpClient.GetCurrentTempAsync(zipCode); return Ok(result);
Note about some tests may be throwaway-able test. My tweet
My colleague says to ask “When will this break?” If it’s not really going to save you from anything you didn’t intentionally do, then don’t do it. (I think it still may be useful for TDD, but maybe you delete it later?)
Now that we have tested that the weather controller is calling the http client code, we need an actual implementation. I’m using Apixu for a free weather api. It was one of the first to jump up in my Bing search.
ApixuClientTests_GetTemp_GivenAZipCode_ReturnsTemp
Assert.IsTrue(false)
=> fail/Redvar result = await apiuxClient.GetCurrentTempAsync(zipCode);
GetCurrentTempAsync
method
return Task.FromResult(65.4)
;Assert.AreEqual(fakeTemp, result)
var response = await httpClient.GetStringAsync($"current.json?key={apiKey}&q={zipCode}");
var weather = ApiuxWeatherCurrentResponse.FromJson(response);
return weather.Current.TempF;
It’s time to stop, but we are having so much fun!!
Check out the MS Docs on unit testing Controllers in .Net Core .
You need to cast the actionResult
to an OkObjectResult
or NotFoundObjectResult
or BadRequestObjectResult
. var result = actionResult as OkObjectResult;
then Assert.AreEqual(1, result.Value);
This would probably be a better video, so I’d recommend the MVA course and/or following along with Kent Beck’s TDD book.
I didn’t get to this, we’ll see what the future holds.
Testing with InMemory in EF Core avoids the need to Fake out EF parts and can help make sure all the LINQ statements are correct. However, it is a bit slower then faking and you have to do a manually add before a .Include usage, so the choice is yours.
Here are a few hints:
Factory
public class SettingsControllerTests
{
private static (SettingsController settingsController, SettingsDbContext settingsDbContext) Factory(string testDbName)
{
var dbContext = SetupDatabase(testDbName);
var loggerFake = new Mock<ILogger<SettingsController>>();
return (new SettingsController(dbContext, loggerFake.Object), dbContext);
}
....
Add a SetupDatabase method.
private static SettingsDbContext SetupDatabase(string testDbName)
{
var options = new DbContextOptionsBuilder<SettingsDbContext>()
.UseInMemoryDatabase(databaseName: testDbName)
.Options;
return new SettingsDbContext(options);
}
Use in your test
[TestMethod]
public async Task SettingsController_Get_ReturnsValuesAsync()
{
// Arrange
var (controller, settingsDbContext) = Factory(System.Reflection.MethodBase.GetCurrentMethod().Name);
var setting = new Setting
{
Id = 1,
Name = "test1",
Value = "valueTest1",
Description = "descriptionTest"
};
settingsDbContext.Settings.Add(setting);
settingsDbContext.SaveChanges();
// Act
var actionResult = await controller.Get(CancellationToken.None);
var result = actionResult as OkObjectResult;
var value = result.Value as IList<Setting>;
// Assert
Assert.AreEqual(1, value.Count);
}
I didn’t get to this, we’ll see what the future holds.
I’d like to build a few demo tests for VueJs, using Jest and running them in KarmaJs on multiple browsers.
In an effort to reduce duplication see my original post on Unit Testing .
00:00 Introduction and sponsor thank you 01:00 Terms 02:00 Three Main Camps 04:00 A Journey, My Experiences 08:00 A few whys 10:00 What do I test? 12:00 Where to start? 15:00 Create a Test Seam - Http Call 20:00 Getting Started - a walkthrough 25:00 API Test - Using MOQ DEMO 46:00 Resources 48:00 Ready to Go? 50:00 Questions & Thank you 55:00 done!
(aka if only there was more time :-))
I recorded the October 27th, 2018 talk on my Pixel phone. It turned out pretty good. Here is the raw audio .
Please consider using Brave and adding me to your BAT payment ledger. Then you won't have to see ads! (when I get to $100 in Google Ads for a payout, I pledge to turn off ads)
Also check out my Resources Page for referrals that would help me.