All Articles

CoreLMS: Entities, Services, and TDD, Oh My!

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

Check out the project on Github

Watch the Replay

Skip to the Good Stuff

  • Start of code/adding entities 2:20
  • Services and TDD 1:50:46

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 a Person 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.

  1. Write the test
  2. See the test fail (even to compile at this stage)
  3. Write the code under test
  4. See the test pass
  5. 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:

  1. I’ve ordered Test Driven Development: By Example by Kent Beck. Apparently the Bible of TDD.
  2. 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