I had come across a scenario where I needed to store certain files to a certain location on a server. This certain location, basically acts like a shelf to hold these files. Just like a neatly arranged shelf, all these files were arranged within a directory structure.
On the Server, I created an FTP site listening at the default port (Port 21).This FTP Server was mapped to the location of my interest.
If you don’t know how to create an FTP Site, please look here:
http://learn.iis.net/page.aspx/301/creating-a-new-ftp-site/
In Visual Studio, through my C# code I wrote a class which provided two public methods:
1) public String WriteFtpMyFileToServer(Stream fileStream)
2) public Boolean DeleteFtpMyFileFromServer(String fileNamePlusPath)
There were private helper methods as well to remove repeated coding (good practice).
Following is a code snippet from method (1) which would write the file to location of interest:
////// Method to Transfer the file via FTP /// /// The stream on the file to be sent///String public String WriteFtpMyFileToServer (Stream fileStream) { . . . . . //Get the FTP site Uri from ConfigurationManager in string including filepath Uri ftpUri = new Uri(String.Format(CultureInfo.CurrentCulture,"{0}{1}",ConfigurationManager.AppSettings["FtpSite"],filePath)); //Creates an FTP web request FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri); //Setting the request method request.Method = WebRequestMethods.Ftp.UploadFile; //Get the request stream Stream ftpStream = request.GetRequestStream(); //Copy from fileStream to ftpStream int bufferLength = 2048; // 2K byte[] buffer = new byte[bufferLength]; int count = 0; int readBytes = 0; //Byte by Byte do { readBytes = fileStream.Read(buffer, 0, bufferLength); ftpStream.Write(buffer, 0, readBytes); count += readBytes; }while (readBytes != 0); // Close both the streams fileStream.Close(); fileStream.Dispose(); ftpStream.Close(); // Send the file and Get Response FtpWebResponse response = (FtpWebResponse)request.GetResponse(); //Check the result of our upload and see if successful if (response.StatusCode == FtpStatusCode.ClosingData) { // Close the ftp session response.Close(); fileSentSuccessfully = true; } . . . //Return String: Message of what happened or Null . . . }
Its always a good thing to keep constant Uris (in this case FTP Site) in .config files. This is due to the fact that these Uris may change depending on your environments. It may be different in production, development and test environments. You can use the ConfigurationManager to access from these .config files.
Following is a code snippet from method (2) which would delete the file from the location of interest:
////// This method deletes the specified File via FTP /// /// filepath followed by name///bool sucess/failure public Boolean DeleteFtpMyFileFromServer (String fileNamePlusPathOnServer) { . . . //Creating Uri from string Uri ftpUri = new Uri(String.Format(CultureInfo.CurrentCulture,"{0}{1}",ConfigurationManager.AppSettings["FtpSite"],fileNamePlusPathOnServer)); //Creates an FTP web request FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri); //Method delete file request.Method = WebRequestMethods.Ftp.DeleteFile; // Send the command and Get Response FtpWebResponse response = (FtpWebResponse)request.GetResponse(); //Check the result of our upload and see if successful if (response.StatusCode == FtpStatusCode.FileActionOK) { // Close the ftp session response.Close(); return true; } return false; }
Personally, I have always believed that throwing exceptions is a really good practice. Even though you don’t see any exceptions thrown in the code snippets above, you can always add code to throw exceptions to validate the parameters to the methods and for any other failure. This really helps to convey messages from the callee class to the caller class about what went wrong.
When this class is live, we would expect a real file stream to be passed to method (1). But how will you write unit tests for these methods?
Writing unit tests for each possible scenario in your methods is the best way for documenting and portraying what is expected from the method you just wrote. It validates your logic in the method, checks whether the method indeed returns what you expect and if the method is working the way you want it to. Overall, it makes the code more robust and reliable. It also helps other developers to understand what you intended to do in the method. Test Driven Development (TDD) and Code Coverage are really good techniques for the same.
If you don’t know what TDD is, please look here:
http://en.wikipedia.org/wiki/Test-driven_development
If you don’t know what Code Coverage is, please look here:
http://en.wikipedia.org/wiki/Code_coverage
To write a test for method (1), I needed to create a file in my test and pass its stream to method (1). It made no sense to create a file each time locally at a certain path and use it for test purposes. This is due to the fact that the underlying directory structure may change from host to host, depending on where the tests are run. Each test should be written independent of the location, environment and of other tests.
This is where resource files come in. A resource file can be any data such as text files, images, audio or video that you application might require. Once a resource file is added to the project, it always stays with the project just like other .cs files (your code).
In my test project, I created a folder within the project called “Resources” and added a text file to it with a test name called “TestFile.txt”. This file had some junk data in it. I clicked on the file and in the Properties dialog below it, changed the Build Action to “Embedded Resource”.
To get the stream of this file in your test class:
///This way, the tests can be run on any machine without worrying about the underlying directory structure of the machine./// Summary description for Your Test Class /// [TestClass] public class YouTestClassName { #region Private Variables . . . //Assembly giving reference to Resource File private static Assembly _thisAssembly; //File Stream of the file to be transferred (Resource Text File) private static Stream _TestFileStream; . . . #endregion Private Variables #region Test Context //////Gets or sets the test context which provides ///information about and functionality for the current test run. /// public TestContext TestContext { get; set; } #endregion Test Context #region Additional test attributes // You can use the following additional attributes as you write your tests: // // Use ClassInitialize to run code before running the first test in the class [ClassInitialize] public static void MyClassInitialize(TestContext testContext) { . . . //Getting the assembly _thisAssembly = Assembly.GetExecutingAssembly(); . . . } // Use ClassCleanup to run code after all tests in a class have run [ClassCleanup] public static void MyClassCleanup() { . . . } //// Use TestInitialize to run code before running each test //[TestInitialize()] //public void MyTestInitialize() //{ //} //// Use TestCleanup to run code after each test has run //[TestCleanup()] //public void MyTestCleanup() //{ //} #endregion Additional test attributes ////// This method tests the FtpFile method successfully /// [TestMethod] public void FtpFileTest_Success() { //ARRANGE //Getting the file stream if (_thisAssembly != null) { //Getting the stream from resource file _TestFileStream = _thisAssembly.GetManifestResourceStream("Namespace.TestFile.txt"); } //ACT //Calling the WriteFtpFileToServer method (METHOD(1)) //_sut is the instance of the class which provides these methods _returnString = _sut.WriteFtpFileToServer(_TestFileStream); //ASSERT Assert.IsNotNull(_returnString); } . . . //More tests . . . }
0 comments:
Post a Comment