【发布时间】:2021-06-11 00:22:56
【问题描述】:
为了正确地对我的一些类进行单元测试,我需要模拟正在测试的主类使用的类对象。如果正在使用的所有对象都实现了接口并且我只需要使用该接口的方法,这是一个简单的过程。然而,问题来了:
接口是其他开发人员对该类的期望的约定。因此,所有接口方法、属性等都是公共的。任何好的代码也应该有好的封装。因此,我不想将我的类中的所有方法都公开,因此不要在接口中声明它。因此,我无法模拟和设置这些在我的主类中使用的内部方法。
我研究的其他选项是使用可以具有不同访问修饰符的抽象类,并且我可以在其中包含正确的方法是内部的或私有的等。但是由于我希望被模拟的类可用是一个公共属性接口类型供其他开发人员使用,我需要它是一个接口,但现在它不再兼容,因为我的主类不能再调用内部方法,因为它定义为接口。第 22 条规则。(如果您想知道,使用 virtual for 方法会遇到与使用抽象类相同的问题)。
我搜索了有关 C# 模拟的类似问题,但没有找到与我相同的问题。并且请不要对“C# 不是用于测试的好语言”之类的东西持哲学态度。这不是答案。
有什么想法吗?
我添加了这个代码示例,以便更容易看到上述问题。
[assembly: InternalsVisibleTo("MyService.Tests")]
public interface IMyService
{
MethodABC()
...
}
public class MyService : IMyService
{
public void MethodABC()
{
...
}
internal void Initialize()
{
...
}
...
}
public sealed partial class MyMain
{
public IMyService Service { get; private set; }
private MyService _service;
...
private void SomeMethod()
{
// This method is in interface and can be used outside the project when this assembly is referenced
_service.MethodABC()
...
// This method is and internal method inside the class not to be seen or used outside the project.
// 1) When running this method through unit test that mocked the IMyService will fail here since initialize don't exist which is correct.
// 2) Mocking the MyService will require "virtual" to be added to all methods used and don't provide a interface/template for a developers if
// they want to swap the service out.
// 3) Changing the interface to abstract class and mocking that allows for making this an internal method and also set it up in mock to do
// something for unit test and provides contract other developers can implement and pass in to use a different service, but requires
// "override" for all methods to be used from "outside" this assembly. This is best solution but require changes to code for sole purpose
// of testing which is an anti-pattern.
_service.Initialize()
...
}
}
// Unit test method in test project
[TestClass]
public class MyMainTests
{
private Mock<IMyService> _myServiceMock = new Mock<IMyService>();
[TestMethod]
public void MyMain_Test_SomeMethod()
{
...
SomeMethod()
...
}
}
【问题讨论】:
-
提供minimal reproducible example 的代码可能有助于显示您的问题(例如,您当前拥有的类结构以及您要进行单元测试的代码)
-
这不是一个需要代码示例的问题。如果您不知道如何模拟接口或抽象类,那么您将无法解决问题。也许你不喜欢阅读,所以我总结一下。我需要模拟一个内部方法,同时将对象公开为一个接口,而不是仅仅为了测试目的而添加代码。
-
你总结的方式与我在你原来的问题中读到的不同,但是模拟内部方法似乎违背了模拟框架的目的。
-
我不确定我是否完全理解您所描述的问题,但是当我想表明接口主要是作为开发细节或作为注入点时,我'已将其声明为“内部接口 {}”。然后只有该程序集可以访问它(除非您还使用 InternalsVisibleTo 属性)。 “合同”仍然存在,但不能在依赖项目中使用。
-
我尝试添加代码示例以使问题更清晰。代码比英文更容易理解:)
标签: c# .net unit-testing interface moq