Skip to content

[TUnit.Mocks] Generated mock wrapper type names collide across namespaces (e.g. two different ICluster interfaces) - one silently loses its members #6494

Description

@samtrion

Description

T.Mock() generates a wrapper type named after the mocked type's short name only (e.g. IClusterMock for an interface named ICluster), without namespace-qualifying the generated type name. When two distinct types with the same short name (but different namespaces) are both mocked in the same compilation, this causes a collision: one of the two generated wrappers ends up missing the real interface's members entirely, with no compiler error pointing at the actual cause.

Repro

<PackageReference Include="CassandraCSharpDriver" Version="3.22.0" />
<PackageReference Include="CouchbaseNetClient" Version="3.9.4" />
<PackageReference Include="TUnit.Mocks" Version="1.61.38" />
using TUnit.Mocks;
using CassandraDriver = global::Cassandra;

var couchbaseCluster = Couchbase.ICluster.Mock();

var cluster = CassandraDriver.ICluster.Mock();
var session = CassandraDriver.ISession.Mock();
_ = cluster.ConnectAsync().Returns(session); // fails

Both Cassandra.ICluster and Couchbase.ICluster are interfaces named ICluster in different namespaces. Mocking either one alone works fine. Mocking both in the same compilation breaks Cassandra.ICluster's generated wrapper (in this ordering) — its ConnectAsync setup method is simply absent from the wrapper, even though the interface itself does declare it and mocking it in isolation exposes it correctly.

Environment

  • TUnit.Mocks: 1.61.38 (latest at time of filing)
  • CassandraCSharpDriver: 3.22.0
  • CouchbaseNetClient: 3.9.4
  • TFM: net10.0
  • LangVersion: preview (C# 14)
  • OS: Windows 11

Actual error

Program.cs(8,13): error CS1061: 'IClusterMock' does not contain a definition for 'ConnectAsync',
and no accessible extension method 'ConnectAsync' accepting a first argument of type 'IClusterMock'
could be found (are you missing a using directive or an assembly reference?)

Note there is no error/diagnostic pointing at a name collision — the symptom looks exactly like "this interface member isn't supported," which is misleading; the interface is fine in isolation.

Suspected cause

The source generator's naming scheme for generated mock wrapper types appears to use only the mocked type's simple name (IClusterIClusterMock) rather than a namespace- or assembly-qualified name (e.g. Cassandra_ICluster_Mock / Couchbase_ICluster_Mock, matching the naming convention already used for the generated file names, e.g. Cassandra_ICluster_Mock.g.cs). With two same-named interfaces mocked in one compilation, this produces two conflicting partial/duplicate type declarations, and one silently loses.

Impact

Any project that mocks two same-named types (interfaces or classes) from different libraries/namespaces in the same test assembly will have one of them silently broken, with an error message that looks like an unrelated "member not supported" issue rather than a naming collision — likely to cost other users significant debugging time, as it did us.

Context

Found while migrating an OSS health-checks library (dailydevops/healthchecks) from NSubstitute to TUnit.Mocks across ~65 test files — we mock both Cassandra.ICluster (Cassandra/CassandraHealthCheckTests.cs) and Couchbase.ICluster (Couchbase/CouchbaseHealthCheckTests.cs) in the same test project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions