【发布时间】:2021-08-26 10:17:15
【问题描述】:
我遇到了一个我不确定在 C# 中是否可行的情况,但我还是想问一下。我需要从具有两个具体子类的类库中公开一个类型,并且我不希望用户能够创建子类的新实例 - 但是,如果我将所有内容都设为内部,那么用户将无法为测试目的创建实例。
我目前的情况是这样的(所有示例都已简化,但可以理解):
public abstract class Result
{
internal Result() { }
}
internal class SuccessResult : Result { }
internal class FailureResult<TError> : Result
{
TError Error { get; init; }
}
库的用户可以访问如下一对接口,一次提供一个接口,从而限制可以创建的结果类型:
public interface IPartialResultFactory
{
Result Success();
}
public interface IResultFactory : IPartialResultFactory
{
Result Error<TError>(TError error);
}
执行上述操作我可以确保只有有权访问该程序集的内部类的代码才能创建 Result 的新子类型,但是当有人测试他们与库的集成时,他们将无法测试代码很容易,因为他们无法创建 Result 类的新实例。
我目前正在使用的解决方案是重新定义Result 类,如下所示:
public abstract class Result
{
internal Result() { }
public static Result Success() => new SuccessResult();
public static Result Error<TError>(TError error) => new ErrorResult<TError>(error);
}
但是这有两个问题:
-
首先它引入了
Result和它的子类之间的耦合,如果可能的话,我想避免在将来的某个时间点添加其他子类; -
其次,这意味着在向用户提供
IPartialResultFactory实例的情况下,他们仍然可以返回ErrorResult实例,我想限制他们只返回SuccessResult。
所以,总而言之,有没有一种方法可以让用户测试与我的代码的集成,创建 Result 的实例,但随后限制如何为实际集成创建这些实例?
【问题讨论】:
-
你想创建类似代数数据类型的东西吗?
-
我不确定我是否真的理解:您想要内部子类但允许从另一个程序集中获取它们的实例以进行测试?在我看来,这是不可能的,正是因为封装级别的限制,这里是内部的......因此即使使用接口,您也无法获得可编译的代码,除非可能绕过 OOP 的约束通过使用反射或 IL 代码注入来实现逻辑。只是说,我从来没有涉足过这种事情。您需要在测试人员和这些类之间提供一个测试公共连接器。
-
@Sweeper 在一定程度上是的,但也不是。在内部,我正在将
Result的实例转换为 OneOf monad 的简单内部实现(类似于不相交的联合),但是目的是使暴露的部分尽可能地地道,并且在这里使用 OneOf monad 会由于嵌套泛型已经很乱了,我希望在不破坏现有用法的情况下添加更多Results 的可能性。 -
@Olivier Rogier 我认为我同意我认为这是不可能的,至少在没有一些非常时髦的咖啡怪异的情况下是不可能的。你关于测试连接器的观点很有趣,我不确定这是否会作为 NuGet 包发布,但添加第二个包来测试它可能是一个想法,它允许构造
Result实例。如果有人想特意在非测试代码中使用标记为测试的包,那么就在他们身上。 -
您是否有理由不为您的 Result 类公开接口类型?
标签: c# interface integration-testing .net-assembly encapsulation