Test Class Without Test

ID

csharp.test_class_without_test

Severity

critical

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

CSharp

Tags

coverage, reliability, testing

Description

Reports a class that presents itself as a test fixture — it carries a fixture attribute such as [TestFixture] or [TestClass], or its name ends in Test or Tests — yet declares no test method at all.

Not reported: abstract, static and partial classes; a class whose nested types hold the tests; a class deriving from a base class declared in this file that declares the test itself, directly or through a further base, walking the chain as far as the file resolves it; and a class deriving from a base class the file does not declare at all — an unseen base may be the one that declares the tests, and analysis confined to one file cannot rule that out (the shape of a generic fixture base such as ServiceTest<TEntity> that lives in its own file, with subclasses only overriding a data property). Also not reported: a concrete class that looks like a shared fixture base itself — it wires up lifecycle hooks ([SetUp], [TearDown], …) and exposes state through a protected member, the shape a base class takes so its subclasses, possibly declared in other files, can build on it — and, more narrowly, a class that another class in the same file derives from. When the recognition rests on the class name alone, a class exposing public data members is left alone too — that is the shape of a domain type that merely happens to be named …Test, such as a BloodTest, not of a fixture.

This rule applies to test code only.

Rationale

A fixture with no tests contributes nothing to the run. The suite still lists the class, the file still reads as coverage for the area it names, and the setup it holds still looks purposeful — but nothing in it is ever executed as a test. Whatever the class was written to cover is uncovered, and the gap is invisible precisely because the class looks like a passing test.

The usual causes are a fixture whose test attributes were dropped in a framework migration (moving from NUnit’s [Test] to xUnit’s [Fact], say), a class left behind after its tests were moved elsewhere, and setup scaffolding that was written before the tests and never followed by them.

[TestFixture]
public class InvoiceTests                   // FLAW - only setup, no test
{
    private Invoice invoice;

    [SetUp]
    public void Setup() { invoice = new Invoice(); }
}

public class PaymentTests                   // FLAW - named as a fixture, holds only a helper
{
    private void ArrangeGateway() { }
}

[TestFixture]
public class ShippingTests                  // OK
{
    [Test]
    public void RateIsComputed() { Assert.That(Shipping.Rate(1), Is.EqualTo(1)); }
}

[TestFixture]
public abstract class RepositoryTests       // OK - an abstract fixture base
{
    [SetUp]
    public void Init() { }
}

public class ContractTests                  // OK - the fixture below derives from it
{
    protected void Arrange() { }
}

public class ExtendedContractTests : ContractTests
{
    [Test]
    public void Signs() { Assert.That(2, Is.EqualTo(2)); }
}

public class BloodTest                      // OK - a domain type that merely ends in Test
{
    public string Patient { get; set; }
    public double Value { get; set; }
}

[TestFixture]
public class DiscountServiceTests : ServiceTestBase<Discount>   // OK - base declared elsewhere
{
    protected override CrudData<Discount> CrudData { get { return _data; } }
}

public class GatewayTest                    // OK - base of subclasses declared in other files
{
    protected PaymentGateway gateway;

    [SetUp]
    public void BeforeEachTest() { gateway = new PaymentGateway(); }

    [TearDown]
    public void AfterEachTest() { gateway.Reset(); }
}

Remediation

Decide what the class is. If its tests lost their attributes in a migration, restore them. If the tests moved away, delete the leftover class so the suite stops advertising coverage it does not have. If it is a shared fixture base, make it abstract — that states the intent and takes it out of the runner’s discovery. If it is not a test at all, rename it so it no longer reads as one.