I recently wrote a little function that takes a screenshot at the end of a test if it has errored. What sounded very simple at the start turned out to be quite a bit of work, and quite a few lines of code to handle certain scenarios! It's now over 50 lines of code!
I'll start with what I had at the beginning, this was to simply take a screenshot in the working directory, we are using SpecFlow and Selenium to run the tests, so we are going to check if the ScenarioContext.Current.TestError isn't null, if it is, then using Selenium, take a screenshot (note the below code is a simplified version of what I had at the beginning).
This worked fine, but I noticed that not every error was being taken, I was racking my brains, and thought it coudl possibly be 2 things, the file name could be containing illegal characters (this createFileName took the ScenarioContext.Current.ScenarioInfo.Title as the file name, so there was a high chance that this contained illegal filename characters, and sure enough, upon further investigation some tests did have illegal characters.
So I googled for a method that would remove illegal characters from a string, and sure enough, Google delivered! With a bit of modifications I got the below code to work:
This was placed inside the createFileName method, and took the ScenarioContext.Current.ScenarioInfo.Title as a parameter and removed any illegal characters.
The other issue that it might have been was that the file name could have been greater than the maximum allowed in Windows, so whilst it could have been either one of these things, I thought it would be best to guard against both, as either one could come up again.
This was created by passing in the whole working directory and the clean file name, and if it was greater than 255 characters it would trim it so that it wasn't longer anymore. The following code achieved this:
This would return the new filename with the end truncated if at all necessary.
This made my code far more robust, and I like to think that being a tester I am more aware of possible issues.
I thought this was good to go, however, upon further investigation, if we have a scenario that has multiple examples, then it would only create 1 screenshot, I needed a method that if the file exists, then append a numerical value to the end of the filename, so as to have multiple screenshots for multiple scenario examples. This was pretty simple to achieve, though it meant I would have to reduce the maximum value of a filename by one, to accomodate adding an integer to the end of the filename:
I won't bore you by going through the code :)
We are almost there, however my code originally was only creating the directory on the test error if it didn't exist, and you'd end up with multiple test run screenshots in there if you ran it locally (on the build server it's fine as each build got it's own drop folder to store stuff in). To get around this I would create or empty the directory (depending if it exists or not) at the start of the test run, to get rid of having a folder full of multiple screenshots.
[BeforeTestRun]
public static void SetupScreenShotDirectoryBeforeTestRun()
{
var workingDirectory = Directory.GetCurrentDirectory();
var screenshotDirectory = Path.Combine(workingDirectory, "errorScreenshots");
var fileInfo = new DirectoryInfo(screenshotDirectory);
if (Directory.Exists(screenshotDirectory))
{
foreach (FileInfo file in fileInfo.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in fileInfo.GetDirectories())
{
dir.Delete(true);
}
}
if (!Directory.Exists(screenshotDirectory))
{
Directory.CreateDirectory(screenshotDirectory);
}
ErrorScreenshotDirectory = screenshotDirectory;
}
This would empty the folder if it already exists, if not then it creates it in the working directory at the start of each test run.
As a nice to have, I also added in some code that will put the error message at the top of the image, this means that you can cycle through the images and see the error message without having to compare it to a test run. I found that .net has the ability to do this, and found the below code on google:
So there you have it, what started out as something simple, (just like most things), quickly became something much bigger, when I thought about it from a QA perspective, maybe a developer would have noticed these things too, but I like to think that by coming from a testing background I am more open to finding bugs in my code! However I'm sure you'll agree it's made it a lot more robust and has the ability to handle pretty much anything that the tests can throw at it (I hope!).
I'll start with what I had at the beginning, this was to simply take a screenshot in the working directory, we are using SpecFlow and Selenium to run the tests, so we are going to check if the ScenarioContext.Current.TestError isn't null, if it is, then using Selenium, take a screenshot (note the below code is a simplified version of what I had at the beginning).
[AfterScenario] public static void TakeScreenShotOnError() {So if you follow the code through, it will run after a scenario has been executed (SpecFlow) and if there isn't an error then it will do nothing, if there is then it will initiate the ITakesScreenshot, and use that to take a screenshot. There are a number of methods inside this piece of code, but I will go through them in more detail as we go on the journey to create the functionality and make it robust.
if (ScenarioContext.Current.TestError == null) return;
var screenshotDriver = Driver as ITakesScreenshot; if (screenshotDriver != null) { var screenshot = screenshotDriver.GetScreenshot();
string fileName = createFileName(ErrorScreenshotDirectory);
screenshot.SaveAsFile(Path.Combine(ErrorScreenshotDirectory, fileName + ".png"), ImageFormat.Png);
}
This worked fine, but I noticed that not every error was being taken, I was racking my brains, and thought it coudl possibly be 2 things, the file name could be containing illegal characters (this createFileName took the ScenarioContext.Current.ScenarioInfo.Title as the file name, so there was a high chance that this contained illegal filename characters, and sure enough, upon further investigation some tests did have illegal characters.
So I googled for a method that would remove illegal characters from a string, and sure enough, Google delivered! With a bit of modifications I got the below code to work:
private static string CleanFileName(string fileName) { return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty)); }
This was placed inside the createFileName method, and took the ScenarioContext.Current.ScenarioInfo.Title as a parameter and removed any illegal characters.
The other issue that it might have been was that the file name could have been greater than the maximum allowed in Windows, so whilst it could have been either one of these things, I thought it would be best to guard against both, as either one could come up again.
This was created by passing in the whole working directory and the clean file name, and if it was greater than 255 characters it would trim it so that it wasn't longer anymore. The following code achieved this:
private static string truncateString(string value, int maxLength) { return value.Length <= maxLength ? value : value.Substring(0, maxLength); }
This would return the new filename with the end truncated if at all necessary.
This made my code far more robust, and I like to think that being a tester I am more aware of possible issues.
I thought this was good to go, however, upon further investigation, if we have a scenario that has multiple examples, then it would only create 1 screenshot, I needed a method that if the file exists, then append a numerical value to the end of the filename, so as to have multiple screenshots for multiple scenario examples. This was pretty simple to achieve, though it meant I would have to reduce the maximum value of a filename by one, to accomodate adding an integer to the end of the filename:
private static string incrementFileName(string workingDirectory, string fileName) { int i = 1; do { i++; } while (File.Exists(Path.Combine(workingDirectory,fileName+i+".png"))); fileName = fileName + i; return fileName; }
I won't bore you by going through the code :)
We are almost there, however my code originally was only creating the directory on the test error if it didn't exist, and you'd end up with multiple test run screenshots in there if you ran it locally (on the build server it's fine as each build got it's own drop folder to store stuff in). To get around this I would create or empty the directory (depending if it exists or not) at the start of the test run, to get rid of having a folder full of multiple screenshots.
[BeforeTestRun]
public static void SetupScreenShotDirectoryBeforeTestRun()
{
var workingDirectory = Directory.GetCurrentDirectory();
var screenshotDirectory = Path.Combine(workingDirectory, "errorScreenshots");
var fileInfo = new DirectoryInfo(screenshotDirectory);
if (Directory.Exists(screenshotDirectory))
{
foreach (FileInfo file in fileInfo.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in fileInfo.GetDirectories())
{
dir.Delete(true);
}
}
if (!Directory.Exists(screenshotDirectory))
{
Directory.CreateDirectory(screenshotDirectory);
}
ErrorScreenshotDirectory = screenshotDirectory;
}
This would empty the folder if it already exists, if not then it creates it in the working directory at the start of each test run.
As a nice to have, I also added in some code that will put the error message at the top of the image, this means that you can cycle through the images and see the error message without having to compare it to a test run. I found that .net has the ability to do this, and found the below code on google:
private static void AppendErrorMessageToImage(string filePNG) { Bitmap bitmap = null; using (var stream = File.OpenRead(filePNG)) { bitmap = (Bitmap)Image.FromStream(stream); }With a bit of modification I got it to grab the ScenarioContext.Current.TestError.Message and print it to the top of the image.
using (bitmap) using (var graphics = Graphics.FromImage(bitmap)) using (var font = new Font("Arial", 20, FontStyle.Regular)) { graphics.DrawString(ScenarioContext.Current.TestError.Message, font, Brushes.Red, 0, 0);
bitmap.Save(filePNG); } }
So there you have it, what started out as something simple, (just like most things), quickly became something much bigger, when I thought about it from a QA perspective, maybe a developer would have noticed these things too, but I like to think that by coming from a testing background I am more open to finding bugs in my code! However I'm sure you'll agree it's made it a lot more robust and has the ability to handle pretty much anything that the tests can throw at it (I hope!).
The information shared was very much useful My sincere Thanks for sharing this post Please Continue to share this kind of post
ReplyDeleteSoftware Testing Training in Chennai
I wondered upon your blog and wanted to say that I have really enjoyed reading your blog posts. Thanks for share to our vision..
ReplyDeleteSelenium Training in Chennai | Software Testing Training in Chennai
nice blog has been shared by you. before i read this blog i didn't have any knowledge about this but now i got some knowledge. so keep on sharing such kind of an interesting blogs.
ReplyDeleteselenium training in bangalore
Excellent content..Thanks for sharing such a valuable information..keep updating..
ReplyDeleteBest Cloud Computing Project Center in Chennai | Best BigData Project Center in Chennai | Best Image Processing Project Center in Chennai
Interesting and informative article.. very useful to me.. thanks for sharing your wonderful ideas.. please keep on updating..
ReplyDeleteBest BE | B.Tech Project Center in Chennai | ME | M.Tech Project Center in Chennai | Best IT Project Center in Chennai
Hi, am a big follower of your blog. I am really happy to found such a helpful and fascinating post that is written in well manner. Thanks for sharing such an informative post. keep update your blog.
ReplyDeleteJava Training Institute in Chennai | DotNet Training Institute in Chennai | Web Designing Training Institute in Chennai
Cloud Computing is one of the latest technology, I searched lot of sites to known about cloud computing but after read your blog know I got lot of information about cloud computing. Thank you for sharing.. Multimedia Training Institute in Chennai | MatLab Training Institute in Chennai | PCB Training Institute in Chennai
ReplyDeleteI feel happy about this blog and I like learning more about this topic. keep sharing your information regularly for my future reference. Multimedia Training Institute in Chennai | MatLab Training Institute in Chennai | PCB Training Institute in Chennai | Hardware & Networking Training in Chennai
ReplyDeleteThe information you have delivered in this post was damn good. Keep sharing your post with efficient news. ALM Training Institute in Chennai | ISTQB Training Institute in Chennai
ReplyDeleteYour article is detailed, thanks to it I solved the problem I am entangled. I will regularly follow your writers and visit this site daily.
ReplyDeleteSummer Courses for Business Administration in Chennai | Best Summer Courses in Porur
I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.
ReplyDeleteQuicktest Professional Training
Very Helpful Post And Explained Very Clearly About All the things.Very Helpful. Coming To Our Self We Provide Restaurant Parts Through Out US At Very Affordable Prices And Also We Offer Same Day Shipping In US.We Offer Only Genuine Products.
ReplyDeleteVery interesting blog which helps me to get the in depth knowledge about the technology, Thanks for sharing such a nice blog..
ReplyDeleteGood discussion.
Six Sigma Training in Abu Dhabi
Six Sigma Training in Dammam
Six Sigma Training in Riyadh
nice post thanks for sharing
ReplyDeleteselenium training centers in Marathahalli
best software testing training institutes in Marathahalli with placements
automation testing courses in Marathahalli
selenium testing course in Marathahalli
software testing institutes in Marathahalli
selenium training in Marathahalli
best selenium training in Marathahalli
selenium course in Marathahalli
Excellent Article. Thanks Admin
ReplyDeleteVMware Training in Chennai
DevOps Training in Chennai