使用 rhino mocks 模拟没有接口的服务

Mock service without interfaces using rhino mocks

背景 我的应用程序正在通过代理使用 WCF 服务。我必须对我的实现进行单元测试,确保它使用服务并正确完成处理。

待测方法

public class MyClass
{ 
 private ManagerServiceClientImpl myclient;

 public void MethodToBeTested();
 {
    var result = GetServiceData();
    if(result!=null)
       //some processing
 }
}

private MyObject GetServiceData()
{
    myclient = ServiceLocator.Current.GetInstance<ManagerServiceClientImpl>();
    if(myclient.ConnectToService() && myclient.MyServiceClient.IsConnected)
       return myclient.GetData();
    else
     return null;
}

此为外源提供,本人无权修改

public class ManagerServiceClientImpl
{
  public ServiceClient MyServiceClient { get; private set; }
  public bool ConnectToService()
}

我如何模拟 ManagerServiceClientImpl 它没有接口或方法未标记为虚拟

到目前为止我尝试了什么。

[TestMethod]
public void IsServiceConnected_GetData()
{

  //Arrange
  ManagerServiceClientImpl clientImpl = 
               MockRepository.GenerateMock<ManagerServiceClientImpl>();

  ServiceLocator.Expect(x => x.GetInstance<ManagerServiceClientImpl>())
                .Return(clientImpl);

  var testData= new MyObject 
  {
            ID = "Test1",
            Name ="test",
  }

  //Act
  _myClass.MethodToBeTested();

  //Assert
  stubService.AssertWasCalled(h => h.SaveAllChanges());
}

注:使用Rhino.Mocks。这是我第一次使用 Rhino 模拟

为了使用 RhinoMocks(或 Moq,或任何其他 "constrained" 模拟框架),您正在模拟的类型必须支持对您要模拟的成员的继承。这意味着它必须是一个接口,或者成员必须是 virtual/abstract。否则,这些框架将无法执行它们在运行时生成代理中间人所需执行的操作。有关详细信息,请参阅我的博客 post,了解 .NET 模拟框架如何在幕后工作:https://www.wrightfully.com/how-net-mocking-frameworks-work

可以做的是在您自己的 class 中创建方法来包装其他服务,这样您就可以模拟您的方法并拥有它们 return无论你需要什么,完全绕过服务。

正如 Amittai Shapira 提到的,您可以使用支持它的单元测试框架在没有接口的情况下模拟它,我正在使用 Typemock Isolator 并为您的代码创建了一个示例测试:

我已经创建了一个 MyClass 的实例,并使用 Typemock 的一个功能来模拟非 Public 方法来更改 GetServiceData

的 return 值
[TestMethod]
public void TestMethod1()
{
    var testData = new MyObject
    {
        ID = "Test1",
        Name = "test",
    };

    var realObj = new MyClass();
    Isolate.NonPublic.WhenCalled(realObj, "GetServiceData").WillReturn(testData);
    Isolate.NonPublic.WhenCalled(realObj, "SaveAllChanges").CallOriginal();

    realObj.MethodToBeTested();

    Isolate.Verify.NonPublic.WasCalled(realObj, "SaveAllChanges");
}