If you want to build up your tolerance for looking like an idiot (everyone should), try implementing something you're just beginning to learn live on Twitch....
Oof.
Get the Git
FitzyCodesThings / core-lms
An open source online learning management system project in ASP.Net Core MVC. Explores many best practices and patterns in modern software development.
Watch the Replay
Skip to the Good Stuff
Project Update
During yesterday's stream, I managed to flesh out several more of the entities I'll be using in the open source learning management system I'm developing live.
We got:
-
Course
entity fleshed out-
CourseLesson
added-
CourseLessonAttachment
added
-
-
-
Person
added -
Author
added (which includes aPerson
record, as well) -
AuthorCourseLesson
many-to-many entity added
Then I embarrassed myself. Hard.
I'm still an absolute neophyte when it comes to TDD and unit testing in general.
I knew that.
So why did I think I'd just pop a service AND a test in live with no trouble?
🤷♂️
Anyhoo. Thank goodness for great community members and chatters. TheGrumpyGameDev (another great live code streamer) was there to rescue me and literally hold my hand through writing my first proper TDD test (on this project).
First he stopped me from even creating the ICourseService
interface.
In TDD (the proper way), you START. WITH. THE. TEST.
His question to me was, "How do you know what your interface should define?"
The answer: your unit test TELLS you what functionality you should implement.
- Write the test
- See the test fail (even to compile at this stage)
- Write the code under test
- See the test pass
- Refactor/move on
And always in the smallest measures possible.
It's the Red-Green-Refactor pattern, but to a more minute level than I'd considered previously.
So. We (I) fumbled and we (he) got me there.
Here's the first TRULY properly arranged TDD test I've ever written.
[Fact]
public async Task GetCoursesAsync_ShouldReturnExpectedCourseList()
{
// given (arrange)
List<Course> databaseCourses = new List<Course>();
databaseCourses.Add(new Course()
{
Id = 1,
Name = "Course #1"
});
databaseCourses.Add(new Course()
{
Id = 2,
Name = "Course #2"
});
this.appDbContextMock.Setup(db =>
db.SelectCoursesAsync())
.ReturnsAsync(new List<Course>(databaseCourses));
// when (act)
List<Course> actualCourses = await subject.GetCoursesAsync();
// then (assert)
// 1. Actual list of courses == expected courses
// 2. DB was hit once (and no more)
actualCourses.Should().BeEquivalentTo(databaseCourses);
appDbContextMock.Verify(db => db.SelectCoursesAsync(), Times.Once);
appDbContextMock.VerifyNoOtherCalls();
}
And here's the code it tested (written entirely AFTER the test was written and failing):
public async Task<List<Course>> GetCoursesAsync() => await db.SelectCoursesAsync();
If you're new to TDD or unit testing, that probably looks absurd.
I mean seriously, WHY would you write 32 lines of code (including comments and new lines, but still) just to test a SINGLE line of code?
Note: the service does call a repository method that just returns a simple dump of the Courses table, but I'm testing business logic, not practically built-in DB functionality
Because now I NEVER, EVER have to worry about whether my SelectCoursesAsync
method is doing what it's supposed to do, no matter what I change in the system.
It STILL seems like overkill. Why would that method ever change to need to be tested again?
It probably won't. But it might.
And as I add more and more and more simple, then more complex business logic to my application, I'll always be able to rely on the suite of tests I develop along the way to be sure I don't introduce a problem with unintended consequences.
Next Steps in TDD
All that said, I'm still scratching the surface in my description of TDD here and in my own understanding of it.
To that end, I'm checking out two great resources recommended to me yesterday:
- I've ordered Test Driven Development: By Example by Kent Beck. Apparently the Bible of TDD.
- I'm gonna watch through the library of videos from JitterTed on TDD (and also check out his upcoming course)
I've been sold on TDD (it's already saved my butt on another project). Now I learn.
Wrapping Up
It was a frustrating but productive stream day for CoreLMS. Next week: more tests and services!
Come hang out, chat, and save my butt, too. I need you. 🤣
- John
Top comments (6)
CoreLMS sounds fantastic, to me, as a self-taught developer. I heard about TDD, now I have the chance to see it in application. And the whole idea of LMS, is great. Keep continuing, I'm a big fan!
Thank you so much! And if you ever have ideas or questions, feel FREE to shoot 'em my way!
Please use bigger fonts or some kind of zoom of your screen to see the code. For the mvc part, are you planning to use some kind of js library for the frontend such as datatables.js or jquery?
I'll try to remember to bump the font up a bit more next time!
And the MVC part will primarily just be the built-in ASP.Net Core MVC (so Razor syntax in the actual view) with some simple jquery for validation, interactivity, ajax, etc. (since it's already there).
I DO want to eventually try out server-side Blazor on this project (and then client side Blazor in a different project) as a SPA. In CoreLMS, I'd like to use it when actually viewing a course (simple, isolated from the rest of the app's functionality, etc.).
Server side blazor is nice. You can even use matblazor or radzen components or just simply bootstrap for design. Btw, what's your primary programming language, c#?
"Primary" as in most used today is C#, though I'm nowhere near mastery of it.
I spent most my career in PHP and Javascript, but I've seen the light. 🤣