CodeBlanks Logo Icon CodeBlanks

Summary - Testing basics

C# course · Advanced · Lesson 72/83

Combine [Fact], [Theory] and Mock to write a calculator test.

Recap: testing basics

This group covered xUnit basics: [Fact] for a single scenario, [Theory] + [InlineData] for parameterized tests, Assert for validation (Equal, True, Throws), and Mock<T> to isolate external dependencies.

A good test has three properties: fast (thousands per second), independent (order does not matter), and readable (the failure reason is clear). Now we apply these in a combined example.

Exercise Combined test for StringUtils

Define FactAttribute and a static Assert class with Assert.Equal<T>, Assert.True(bool), and Assert.Throws<T>(Action). StringUtils.Capitalize(string) throws ArgumentException for empty input; otherwise it returns the text with the first letter uppercased. Write three [Fact] methods in StringUtilsTests: TestCapitalizesLower calls Assert.Equal("Merhaba", StringUtils.Capitalize("merhaba")); TestReturnsSameWhenCapital calls Assert.True(StringUtils.Capitalize("A") == "A"); TestEmptyThrows calls Assert.Throws<ArgumentException>(() => StringUtils.Capitalize("")). Main runs all three and prints "Test passed: Capitalize checks".

Show example solution
Attribute { }. Write static class Assert { public static void Equal<T>(T e, T a) { if (!EqualityComparer<T>.Default.Equals(e, a)) throw new Exception(); } public static void True(bool c) { if (!c) throw new Exception(); } public static void Throws<T>(Action a) where T : Exception { try { a(); } catch (T) { return; } throw new Exception(); } }. Write class StringUtils { public static string Capitalize(string text) { if (string.IsNullOrEmpty(text)) throw new ArgumentException(); return char.ToUpper(text[0]) + text.Substring(1); } }. Write class StringUtilsTests { [Fact] public void TestCapitalizesLower() { Assert.Equal("Merhaba", StringUtils.Capitalize("merhaba")); } [Fact] public void TestReturnsSameWhenCapital() { Assert.True(StringUtils.Capitalize("A") == "A"); } [Fact] public void TestEmptyThrows() { Assert.Throws<ArgumentException>(() => StringUtils.Capitalize("")); } }. In Main, call all three and Console.WriteLine("Test passed: Capitalize checks");
Example solution:
using System;

class FactAttribute : Attribute { }

static class Assert
{
    public static void Equal<T>(T expected, T actual)
    {
        if (!EqualityComparer<T>.Default.Equals(expected, actual))
            throw new Exception($"Expected {expected}, got {actual}");
    }
    public static void True(bool condition)
    {
        if (!condition) throw new Exception("Expected: true");
    }
    public static void Throws<T>(Action action) where T : Exception
    {
        try { action(); }
        catch (T) { return; }
        throw new Exception("Expected exception was not thrown");
    }
}

class StringUtils
{
    public static string Capitalize(string text)
    {
        if (string.IsNullOrEmpty(text)) throw new ArgumentException();
        return char.ToUpper(text[0]) + text.Substring(1);
    }
}

class StringUtilsTests
{
    [Fact]
    public void TestCapitalizesLower()
    {
        Assert.Equal("Merhaba", StringUtils.Capitalize("merhaba"));
    }

    [Fact]
    public void TestReturnsSameWhenCapital()
    {
        Assert.True(StringUtils.Capitalize("A") == "A");
    }

    [Fact]
    public void TestEmptyThrows()
    {
        Assert.Throws<ArgumentException>(() => StringUtils.Capitalize(""));
    }
}

class Program
{
    static void Main()
    {
        var tests = new StringUtilsTests();
        tests.TestCapitalizesLower();
        tests.TestReturnsSameWhenCapital();
        tests.TestEmptyThrows();
        Console.WriteLine("Test passed: Capitalize checks");
    }
}

Exercise [Theory] for StringUtils.Repeat

Define FactAttribute, TheoryAttribute, InlineDataAttribute (with params object[] constructor), and a static Assert class. StringUtils.Repeat(string text, int count) concatenates text count times and returns it. Mark StringUtilsTests.TestRepeat with [Theory] and three [InlineData] rows: ("a", 3, "aaa"), ("ab", 2, "abab"), ("x", 1, "x"). The body calls Assert.Equal(expected, StringUtils.Repeat(text, count)). Main calls the test with ("a", 3, "aaa") and prints "Test passed: Theory Repeat(a,3)=aaa".

Show example solution
Attribute { } and class InlineDataAttribute : Attribute { public InlineDataAttribute(params object[] data) { } }. Write static class Assert { public static void Equal<T>(T e, T a) { if (!EqualityComparer<T>.Default.Equals(e, a)) throw new Exception(); } }. Write class StringUtils { public static string Repeat(string text, int count) { string r = ""; for (int i = 0; i < count; i++) r += text; return r; } }. Write class StringUtilsTests { [Theory] [InlineData("a", 3, "aaa")] [InlineData("ab", 2, "abab")] [InlineData("x", 1, "x")] public void TestRepeat(string text, int count, string expected) { Assert.Equal(expected, StringUtils.Repeat(text, count)); } }. In Main: var t = new StringUtilsTests(); t.TestRepeat("a", 3, "aaa"); Console.WriteLine("Test passed: Theory Repeat(a,3)=aaa");
Example solution:
using System;

class FactAttribute : Attribute { }
class TheoryAttribute : Attribute { }
class InlineDataAttribute : Attribute
{
    public InlineDataAttribute(params object[] data) { }
}

static class Assert
{
    public static void Equal<T>(T expected, T actual)
    {
        if (!EqualityComparer<T>.Default.Equals(expected, actual))
            throw new Exception($"Expected {expected}, got {actual}");
    }
}

class StringUtils
{
    public static string Repeat(string text, int count)
    {
        string result = "";
        for (int i = 0; i < count; i++) result += text;
        return result;
    }
}

class StringUtilsTests
{
    [Theory]
    [InlineData("a", 3, "aaa")]
    [InlineData("ab", 2, "abab")]
    [InlineData("x", 1, "x")]
    public void TestRepeat(string text, int count, string expected)
    {
        Assert.Equal(expected, StringUtils.Repeat(text, count));
    }
}

class Program
{
    static void Main()
    {
        var tests = new StringUtilsTests();
        tests.TestRepeat("a", 3, "aaa");
        Console.WriteLine("Test passed: Theory Repeat(a,3)=aaa");
    }
}

Test yourself

  1. Match testing concepts

    [Fact]
    Single scenario
    [Theory]
    Parameterized test
    Assert.Throws
    Error path
    Mock<T>
    Fake dependency
  2. Which attribute marks a parameterized test?

    • [Fact]
    • [Theory]
    • [Setup]
    • [Test]
    Show answer

    [Theory] — Correct. [Theory] + [InlineData] define a parameterized test.

All lessons in Advanced