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 (ICluster → IClusterMock) 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.
Description
T.Mock()generates a wrapper type named after the mocked type's short name only (e.g.IClusterMockfor an interface namedICluster), 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
Both
Cassandra.IClusterandCouchbase.IClusterare interfaces namedIClusterin different namespaces. Mocking either one alone works fine. Mocking both in the same compilation breaksCassandra.ICluster's generated wrapper (in this ordering) — itsConnectAsyncsetup method is simply absent from the wrapper, even though the interface itself does declare it and mocking it in isolation exposes it correctly.Environment
Actual error
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 (
ICluster→IClusterMock) 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) andCouchbase.ICluster(Couchbase/CouchbaseHealthCheckTests.cs) in the same test project.