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.