C# xUnit模拟请求来自IoC容器的新实例

C# xUnit模拟请求来自IoC容器的新实例,c#,unit-testing,moq,xunit,C#,Unit Testing,Moq,Xunit,我正在为使用SSH.Net上传文件的方法编写一些单元测试 该项目是一个WPF应用程序,使用Caliburn.Micro作为MVVM框架,并将以下对象注入到我正在测试的类的构造函数中: private IFileTransferManager _fileTransferManager; public FileUploadViewModel(IFileTransferManager fileTransferManager) : base(eventAggregator) {

我正在为使用SSH.Net上传文件的方法编写一些单元测试

该项目是一个WPF应用程序,使用Caliburn.Micro作为MVVM框架,并将以下对象注入到我正在测试的类的构造函数中:

    private IFileTransferManager _fileTransferManager;

    public FileUploadViewModel(IFileTransferManager fileTransferManager) : base(eventAggregator)
    {
        _fileTransferManager = fileTransferManager;
    }
在测试项目中,我正在模拟IFileTransferManager:

    private Mock<IFileTransferManager> _fileTransferManager = new Mock<IFileTransferManager>();
private Mock_fileTransferManager=new Mock();
但现在我要说的是,在代码中,我需要从IoC容器中请求一个新的IFileTransferManager实例,IoC是Caliburn.Micro中的一个静态类:

    _fileTransferManager = IoC.Get<IFileTransferManager>();

    await _fileTransferManager.UploadFile(connection, file.FullName, destinationPath).ConfigureAwait(false);
\u fileTransferManager=IoC.Get();
wait _fileTransferManager.UploadFile(connection,file.FullName,destinationPath).configurewait(false);

由于我正在重新实例化
\u fileTransferManager

使用
Func
委托注入工厂,因此当前它在Caliburn.Micro.dll中抛出
System.InvalidOperationException
,因此如何重构上述代码以使其可测试

private readonly Func<IFileTransferManager> fileTransferManagerFactory;

public FileUploadViewModel(Func<IFileTransferManager> fileTransferManagerFactory) : base(eventAggregator) {
    this.fileTransferManagerFactory = fileTransferManagerFactory;
}
private readonly Func filetransfermanager工厂;
public FileUploadViewModel(Func fileTransferManagerFactory):基本(eventAggregator){
this.fileTransferManagerFactory=fileTransferManagerFactory;
}
这将允许在上载时根据需要创建尽可能多的实例

//get an instance using factory delegate
var fileTransferManager =  fileTransferManagerFactory();

await fileTransferManager.UploadFile(connection, file.FullName, destinationPath).ConfigureAwait(false); IoC.Get<IFileTransferManager>();
//使用工厂委托获取实例
var fileTransferManager=fileTransferManagerFactory();
await fileTransferManager.UploadFile(连接,file.FullName,destinationPath).ConfigureAwait(false);IoC.Get();

对于单元测试,可以很容易地创建一个函数来提供测试用例所需的尽可能多的模拟,我可能会这样做,假设存在其他限制因素,这意味着您希望尽可能少地更改类的外部细节(注意:我没有测试过,因此可能需要稍微调整)

公共类类类测试
{
//使用Func获取文件管理器。。。
private Func\u filemgr=null;
//有一个属性,我们将在此类中使用该属性来获取文件管理器
公共函数GetFileManager
{
得到
{
//如果我们第一次尝试使用此属性,但未设置它,
//然后将其设置为默认实现。
如果(_fileMgr==null)
{
_fileMgr=()=>IoC.Get();
}
返回文件管理器;
}
设置
{         
//允许设置返回IFileTransferManager的函数
如果(_fileMgr==null)
{
_fileMgr=值;
}
}
}
//这是您最终想要测试的方法。。。
公共异步任务SomeMethodIAmTesting()
{
//不要再这样做了:
//_fileTransferManager=IoC.Get();
//而是这样做。
_fileTransferManager=GetFileManager();
等待文件传输管理器
.UploadFile(连接、文件.FullName、目标路径)
.配置等待(错误);
返回true;
}
}
然后在测试中:

Mock<IFileTransferManager> _fileTransferManager = new Mock<IFileTransferManager>();
var cut = new ClassIAmTesting();
//not used Moq for a long time, but think you have to access .Object to get to the 
//actual implementation of IFileTransferManager?
cut.GetFileManager = () => _fileTransferManager.Object; 

//Do actual tests..
var result = cut.SomeMethodIAmTesting();

//Do assertions...
Mock\u fileTransferManager=new Mock();
var cut=新的ClassIAmTesting();
//很长时间没有使用Moq,但认为您必须访问。对象才能访问
//IFileTransferManager的实际实现?
cut.GetFileManager=()=>\u fileTransferManager.Object;
//做实际测试。。
var result=cut.SomeMethodIAmTesting();
//做断言。。。
我建议采用这种方法是因为:

  • 它提供了一种重写类获取用于测试的
    IFileTransferManager
    的方法
  • 如果不使用此重写,它将“退回”到默认实现,从而保留原始行为-您根本不需要从非测试代码更改对此类的现有调用
  • 它不会更改构造函数或添加新的构造函数,我认为这是一个问题,因为您不只是在中注入IFileTransferManager的实例

一个改进可能是使set
internal
,这将阻止其他项目设置此方法,然后它可以通过
InternalVisibleTo
或类似方式公开,但我正试图保持范围相当紧凑…

使用Func委托注入工厂。我很好奇,如果一个实例已经通过构造函数注入,为什么您需要一个新实例?理想情况下,您不应该像这样直接调用IoC容器。如果类中有一个
IFileTransferManager
注入到构造函数中,那么您就不必担心它了,因为在实例化类进行测试时,您只需直接将您的模拟传递给它。@Nkosi这个问题与另一个问题相关,我也在使用进度条跟踪上载进度,但如果我同时执行多个上载,进度条将无法正常工作。我发现的解决方法是为我正在上载的每个文件请求一个新的IFileTransferManager;从长远来看,通过构造函数直接注入它可能会更好,然后在实例化类进行测试时可以直接使用mock。有时候,在现实世界中,我们不得不用有效的解决方案,而不是优雅的解决方案
Mock<IFileTransferManager> _fileTransferManager = new Mock<IFileTransferManager>();
var cut = new ClassIAmTesting();
//not used Moq for a long time, but think you have to access .Object to get to the 
//actual implementation of IFileTransferManager?
cut.GetFileManager = () => _fileTransferManager.Object; 

//Do actual tests..
var result = cut.SomeMethodIAmTesting();

//Do assertions...