首页 > 解决方案 > Autofixture 使用静态延迟实例化创建对象

问题描述

我有一个正在尝试测试的服务类,但遇到了一些困难

此类具有私有构造函数,因此必须从返回 Lazy _singleton 值的静态 Instance 属性创建它。

public class MyService : IMyService
{
    private static readonly Lazy<IMyService> _singleton = new Lazy<IMyService>(() => new MyService(new InjectedService()));
    public static IPermissionService Instance = _singleton.Value;

    private readonly IInjectedService _injectedService;

    private MyService(IInjectedService injectedService) => _injectedService = injectedService;


    // instance method I want to test
    public void DoSomething()
    {

    }
}

我正在尝试使用 AutoFixture 和 AutoMoq 创建我的对象,使用 Create() 方法,但它一直抱怨我没有公共构造函数。如果我将此构造函数设置为 public,我仍然会收到一个似乎来自 Lazy func 的错误。

有人可以帮忙吗?我可能有不同类型的设计问题。我不知道它是否可以轻松修复。

编辑 1: 我没有任何 IoC 容器,因此我可以将我的服务注册为单例。我试图使用这种方法来模拟依赖服务的注入,这样我就可以用 mock 编写测试。

我已经取得了一些进展,但我也不确定我是否喜欢它......无论如何我会分享我所拥有的

public static class My
{
    private static readonly Lazy<IMyService> _singleton = new Lazy<IMyService>(() => new MyService(new InjectedService()));
    public static IPermissionService Service = _singleton.Value;
}

public class MyService : IMyService
{
    private readonly IInjectedService _injectedService;

    public MyService(IInjectedService injectedService) => _injectedService = injectedService;


    // instance method I want to test
    public void DoSomething()
    {

    }
}

通过这种方式,我可以使用 AutoFixture 构建我的类并冻结注入服务的模拟。

我可以这样使用静态类

My.Service.DoSomething();

在我的测试中

Fixture.Freeze<Mock<IInjectedService>>().Setup(...);
var service = Fixture.Create<MyService>();

service.DoSomething();
// assert my things

标签: c#autofixtureautomoq

解决方案


它不像你可能喜欢的那样使用自动模拟容器,但是属性(混蛋)注入应该能让你到达那里。

根据访问要求,将静态属性标记为internal可能会或可能不会对未来的读者有所帮助,但由于您可以快速进行更改,从而允许在单例模式之外创建实例类,因此您似乎可以接受它。

如果您添加一个静态属性,通过该属性可以注入依赖项,您可以更改单例初始化程序以使用其值(混蛋注入:如果有的话)。

public class MyService : IService
{
  private static readonly Lazy<IService > _singleton = new Lazy<IService >(() => new 
  MyService(MyDependency ?? new BastardizedInjectable()));
  public static IService Instance = _singleton.Value;

  internal static IMyDependency MyDependency 
  { 
    get;
    set;
  }

  private readonly IMyDependency _injectedService;

  private MyService(IMyDependency injectedService) => _injectedService = injectedService;


  // instance method I want to test
  public void DoSomething()
  {
    var stuff = _injectedService.GetStuff();
  }
}

使用此实现,您的测试应该能够执行以下操作:

fixture.Customize<MyService>(c => c.FromFactory(() => MyService.Instance));
var depMock = fixture.Freeze<Mock<IMyDependency>>();
MyService.MyDependency = depMock;
var sut = fixture.Create<MyService>();

混蛋注入通常被称为反模式,或者至少与SOLID中的D (依赖倒置)背道而驰。如果您确信可以依赖此属性从 comp-root 注入,那么您应该硬着头皮删除?? new BastardizedInjectable(). 未来你会感谢你的。


推荐阅读