4

我有从 FTP 服务器获取文件列表的方法,如下所示:

    public List<string> FetchFilesList()
    {
        var request = WebRequest.Create(FtpServerUri);
        request.Method = WebRequestMethods.Ftp.ListDirectory;

        request.Credentials = Credentials;

        using (var response = request.GetResponse())
        {
            var responseStream = response.GetResponseStream();

            using (var reader = new StreamReader(responseStream))
            {
                var fileNamesString = reader.ReadToEnd();
                var fileNames = fileNamesString.Split(
                    Environment.NewLine.ToCharArray(), 
                    StringSplitOptions.RemoveEmptyEntries);

                return fileNames.ToList();
            }
        }
    }

我正在尝试编写 UT,它将访问我的本地目录而不是 FTP。

这是我的UT:

// ftpFetcherTestWrapper is thin wrapper to expose properties to UT
// TODO: For some reason providing credentials like this doesn't work :(
// It also doesn't work for DefaultNetworkCredentials
ftpFetcherTestWrapper.CredentialsExposed = CredentialCache.DefaultCredentials;
// As we are using wrapper we substitude FtpServerUri to our fake-local folder
ftpFetcherTestWrapper.ServerUriExposed = new Uri(directoryName);

var filesList = ftpSFetcherTestWrapper.FetchFilesList();

我收到System.Net.WebException: "Access to the path 'D:\\SomePathToUnitTestsExecutionFolder\\XmlFiles' is denied."

问:是否可以通过 WebRequest 使用的 Windows/网络凭据来访问本地文件夹而不是单元测试中的远程 FTP?

[编辑]

请注意,在提供本地文件路径而不是 ftp 时,我可以测试获取文件内容:

public XDocument FetchFile(string fileName)
{
    var client = new WebClient();
    client.Credentials = Credentials;

    var fileUri = new Uri(FtpServerUri, fileName);
    var downloadedXml = client.DownloadString(fileUri);
    return XDocument.Parse(downloadedXml);
}

我也知道我所做的并不是 100% 定义单元测试,但我仍然认为这是一个很好的测试。

4

3 回答 3

2

测试 fetchFilesList() 及其依赖项(对 Webrequest)称为集成测试。我想你想写一个单元测试,即测试 fetchFilesList() 是专门做的。您不想在单元测试中测试 WebRequest。你应该断言它做了它应该做的事情。为此,您需要封装它并模拟它。我会亲自创建一个配置并返回 WebRequest 的 FtpWebRequestFactory。

private FtpWebRequestFactory ftpWebRequestFactory;

public FtpFetcher(FtpWebRequestFactory ftpWebRequestFactory) {
    this.ftpWebRequestFactory = ftpWebRequestFactory;
}

public List<string> FetchFilesList()
{
    var request = ftpWebRequestFactory.Create(FtpServerUri);

    using (var response = request.GetResponse())
    {
        var responseStream = response.GetResponseStream();

        using (var reader = new StreamReader(responseStream))
        {
            var fileNamesString = reader.ReadToEnd();
            var fileNames = fileNamesString.Split(
                    Environment.NewLine.ToCharArray(),
                    StringSplitOptions.RemoveEmptyEntries);

            return fileNames.ToList();
        }
    }
}

现在在您的单元测试中,使用模拟框架(如moq)为 FtpWebRequestFactory 创建模拟。配置mock返回一个mock请求,配置这个mock请求返回一个mock响应。此时,您可以轻松配置响应以返回正确的流。

于 2012-04-15T17:06:49.420 回答
1

您可能应该将地址方案(即“文件”)添加到请求中的 Uri。否则,.NET 不知道使用哪个协议来访问资源。而且由于您访问的是本地文件,因此在大多数情况下,您已经获得授权,因此无需设置凭据。请尝试以下代码:

    static void Main(string[] args)
    {
        Uri uri = new Uri(@"file:///C:\BuddyBuild\x.xml");
        FetchFilesList(uri);
        Console.ReadLine();
    }

    static List<string> FetchFilesList(Uri uri)
    {
        var request = WebRequest.Create(uri);
        request.Method = WebRequestMethods.Ftp.ListDirectory;
        //request.Credentials = CredentialCache.DefaultCredentials;

        using (var response = request.GetResponse())
        {
            var responseStream = response.GetResponseStream();
            using (var reader = new StreamReader(responseStream))
            {
                var fileNamesString = reader.ReadToEnd();
                var fileNames = fileNamesString.Split(
                    Environment.NewLine.ToCharArray(),
                    StringSplitOptions.RemoveEmptyEntries);

                return fileNames.ToList();
            }
        }
    }

注意Main中的第一行:“file:///path_to_your_file”。

然而,正如Pierre-Henri指出的那样,这对于Unit Test来说看起来并不是一个好的设计。您要测试的“单元”是哪个?FtpWebRequest部分还是您将文件内容拆分为列表的部分?我会将您的 FetchFilesList 方法拆分为 2:

    public static List<String> FetchList(Stream stream)
    {
        using (var reader = new StreamReader(stream))
        {
            var fileNamesString = reader.ReadToEnd();
            var fileNames = fileNamesString.Split(
                Environment.NewLine.ToCharArray(),
                StringSplitOptions.RemoveEmptyEntries);

            return fileNames.ToList();
        }
    }

    public static List<string> FetchFilesList2(Uri uri)
    {
        var request = WebRequest.Create(uri);
        request.Method = WebRequestMethods.Ftp.ListDirectory;

        using (var response = request.GetResponse())
        {
            return FetchList(response.GetResponseStream());
        }
    }

这样,您可以分别对 2 方法进行单元测试。尤其是,您可以使用您喜欢的任何Stream对象(FileStream、MemoryStream、来自 WebResponse 的 Stream...)来测试FetchList(Stream stream )。

编辑:抱歉,我没有意识到您正在尝试通过将 URI 设置为本地目录来列出(本地)目录中的文件。我认为使用 WebRequest/Response 类族不可能做到这一点。将 URI 参数设置为本地路径后,WebRequest.Create 调用返回一个FileWebRequest对象,该对象在内部使用FileWebStream访问本地文件。FileWebStream继承FileStream,并且不能用于目录。顺便说一句,如果将 URI 参数设置为 FTP 地址,则 WebRequest.Create 调用将返回 FtpWebRequest 对象。这也是我认为这不是一个好的单元测试的原因之一:)。也许您可以尝试扩展 FileWebRequest(FtpWebRequest 是密封的)来处理目录列表请求。

于 2012-04-18T15:17:50.467 回答
1

共享该文件夹然后像网络路径一样访问它呢?即 \\my-pc-name\the-folder?

google了一下:http ://www.velocityreviews.com/forums/t78357-webrequest-and-shared-files.html

WebRequest request = WebRequest.Create("http://localhost/tempasp/temp/trace.txt" );
request.PreAuthenticate = true;
request.Credentials = new NetworkCredential("user", "pass", "domain" );

WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader( response.GetResponseStream() );

string line = String.Empty;
while ( ( line = reader.ReadLine() ) != null )
{
    Response.Write( line );
    Response.Write( "<BR>" );
}
reader.Close();
response.Close();

这对你有用吗?

于 2012-04-12T13:23:09.837 回答