我有一个使用第三方FTP库
http://ftps.codeplex.com/的类,我想模拟它,以便我可以单独测试该类,而不是FTP库.我已经做到了,但对我来说感觉很麻烦.详细地说,该类使用AlexPilotti.FTPS.Client.FTPSClient类的这些方法:
public string Connect(string hostname,ESSLSupportMode sslSupportMode) public ulong PutFile( string localFileName,string remoteFileName,FileTransferCallback transferCallback)
委托AlexPilotti.FTPS.Client.FileTransferCallback如下所示:
public delegate void FileTransferCallback( FTPSClient sender,ETransferActions action,string localObjectName,string remoteObjectName,ulong fileTransmittedBytes,ulong? fileTransferSize,ref bool cancel);
您可以看到问题,因为PutFile从FTP库获取委托,该委托也从库中获取两种不同的类型.我决定使用泛型来解耦这些类型.
为了能够创建一个模拟对象,我创建了一个接口,然后创建了一个派生类,它是FTP库功能的包装器.
// Interface so that dependency injection can be used. public delegate void FileTransferCallback<T1,T2>(T1 sender,T2 action,ref bool cancel); public interface IFTPClient<T1,T2> : Idisposable { string Connect(string hostname,System.Net.NetworkCredential credential); ulong PutFile( string localFileName,FileTransferCallback<T1,T2> transferCallback); } // Derived class,the wrapper using FTPSClient = FTPS.Client.FTPSClient; using ETransferActions = FTPS.Client.ETransferActions; public class FTPClient : IFTPClient<FTPSClient,ETransferActions> { public FTPClient() { client = new AlexPilotti.FTPS.Client.FTPSClient(); } public ulong PutFile( string localFileName,FileTransferCallback<FTPSClient,ETransferActions> transferCallback) { callback = transferCallback; return client.PutFile(localFileName,remoteFileName,TransferCallback); } public void dispose() { client.dispose(); } public string Connect( string hostname,System.Net.NetworkCredential credential) { return client.Connect(hostname,credential,FTPS.Client.ESSLSupportMode.ClearText); } void TransferCallback( FTPS.Client.FTPSClient sender,FTPS.Client.ETransferActions action,ref bool cancel) { callback.Invoke(sender,action,localObjectName,remoteObjectName,fileTransmittedBytes,fileTransferSize,ref cancel); } private AlexPilotti.FTPS.Client.FTPSClient client; private FileTransferCallback<FTPSClient,ETransferActions> callback; }
这是一个使用此接口的类,我进行单元测试.我把它剥了一下,所以只使用了Connect方法,但我希望它仍然能说明我的问题.
public class FTPServerConnection<T1,T2> { public void Init( IFTPClient<T1,T2> client,string serverName,string userName,string passwd) { IsConnected = false; ServerName = serverName; UserName = userName; Passwd = passwd; ftpClient = client; Connect(); } public void Connect() { ftpClient.Connect( ServerName,new System.Net.NetworkCredential(UserName,Passwd)); } public string ServerName { protected set; get; } public string UserName { protected set; get; } public string Passwd { protected set; get; } public bool IsConnected { protected set; get; } private IFTPClient<T1,T2> ftpClient; }
最后单元测试:
[TestMethod()] public void FTPServerConnectionConstructortest() { var ftpClient = new Mock<IFTPClient<object,object>>(); var ftpServer = new FTPServerConnection<object,object>(); ftpServer.Init(ftpClient.Object,"1.2.3.4","user","passwd"); Assert.AreEqual("1.2.3.4",ftpServer.ServerName); Assert.AreEqual("user",ftpServer.UserName); Assert.AreEqual("passwd",ftpServer.Passwd); }
解决方法
我认为这只会感觉很乱,因为你已经让第三方代表应对了,但对我来说它看起来像一个非常标准的抽象/模拟方法.
我唯一的评论是FTPServerConnection的结构看起来有点不标准;它的编写方式你必须在无效状态下实例化一个(即一个没有客户端细节的状态)然后调用Init(),它本身调用Connect()(从测试的外观来看,它从不被服务器的客户端调用)类).我会说在FTPServerConnection构造函数中提供Init()的参数更为正常,然后完全删除Init()方法并让客户端执行此操作:
var ftpClient = new Mock<IFTPClient<object,object>>(); using (var ftpServer = new FTPServerConnection<object,object>( ftpClient.Object,"passwd")) { ftpServer.Connect(); }
…但是,关于你抽象FTP库的方式,我认为这是一个很好的方式:)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。