Skip to content

Conversation

psiberknetic
Copy link
Contributor

@psiberknetic psiberknetic commented Dec 12, 2019

This section will talk about how we identify what code should actually be tested. (hint, code coverage is often not the best measurement for this)

@psiberknetic
Copy link
Contributor Author

psiberknetic commented Dec 12, 2019

Many organizations use a certain percentage of code coverage to determine if they are "testing enough" with automated tests. As with many things in life, better has more value than more. Let's talk about how we find where we need to have automated tests.

I use the following phrase to drive my logic. "Look for the predicates".

In software development, a predicate is an expression that returns either true or false. The use of predicates enables the flow of the code to branch. Some predicates are very easy to see, while some are a little more difficult, but these are the places where we should focus our tests.

@psiberknetic
Copy link
Contributor Author

psiberknetic commented Dec 12, 2019

Let's look at some code.

If we look at the Menu class in my WhatShouldITest project, we'll see that our code coverage tool indicates that the class is 0% covered by tests. 0%... that's bad, right? We need to write tests!
image

In actuality, the question we should be asking is "What needs to be tested?" As I scan down through this class, there are no predicates at all; no places where the code can branch. Because of that, I would not write tests around this class.

I like to ask the question "What are we actually testing?" Let's look at a concrete example.

Comment on lines +13 to +19
[TestMethod]
public void Special_AfterConstruction_ShouldReturnValue()
{
var menu = new Menu(null);

menu.DailySpecial.Should().NotBeNullOrEmpty();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I've created a test that checks that the DailySpecial property returns something after construction. That's great, right? Our code coverage for the Menu class definitely went up. It's at 85% now!
image

But what are we actually testing?

public Menu(IMenuItemProvider menuItemProvider)
{
_menuItemProvider = menuItemProvider;
DailySpecial = MenuUtilities.GetDailySpecial(DateTime.Now.DayOfWeek);
Copy link
Contributor Author

@psiberknetic psiberknetic Dec 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test we've written is actually testing line 15 in the code above. The only thing that is happening in this line is an assignment. We're just testing that the .NET assignment operator is working. We don't need to test that. If that's not working, we are going to be having MUCH larger problems. We don't need to cover this line. There is only one path this code can take. There is no branching logic.

Some folks would say that we could rewrite the test to ensure that the CORRECT special is being stored, but this isn't the place for that. The predicate isn't here. Where is it?

Comment on lines +27 to +29
return _dailySpecials.Where(s => s.DayOfWeek == dayOfWeek)
.Select(s => s.Special)
.First();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it is. In the GetDailySpecial method in the MenuUtilities class. specifically the s.DayOfWeek == dayOfWeek portion of the code. That is the expression that will return either true or false, and therefore where the flow of the code can branch.

This is the logic that should be tested. Where the predicate is.

Comment on lines +11 to +14
[TestMethod]
public void GetDailySpecial_ItIsMonday_ShouldReturnHalfPriceTacos()
{
MenuUtilities.GetDailySpecial(DayOfWeek.Monday).Should().Be("Half price tacos!");
Copy link
Contributor Author

@psiberknetic psiberknetic Dec 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here we are testing it. At least, we're testing one of the possible paths. An interesting aside; code coverage is now telling us that this function is 100% covered, but in actuality, we're only testing one of the possible paths through this method. There are seven "happy path" scenarios - one for each day of the week. There is also a scenario we should consider where the DayOfWeek parameter is passed in as null.
image

Maybe code coverage isn't the best measurement...

@psiberknetic
Copy link
Contributor Author

psiberknetic commented Dec 12, 2019

Where should I be looking for predicates and, by extension, places I should be thinking about tests? Some C# examples.

  • If statements
  • Loops
  • Switch statements
  • Some Linq calls

For Linq, some methods can be called both with and without predicates. For example, I would test:
someEnumerable.First()
differently than
someEnumerable.First(i => i.Status == Status.Open)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant