Skip to main content

Coding something simple.... or not! Taking a screenshot on error using Selenium WebDriver

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).

[AfterScenario]        public static void TakeScreenShotOnError()        {
            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);
 }
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.

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);            }
            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);            }                    }
With a bit of modification I got it to grab the ScenarioContext.Current.TestError.Message and print it to the top of the image. 


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!).


Comments

  1. The information shared was very much useful My sincere Thanks for sharing this post Please Continue to share this kind of post
    Software Testing Training in Chennai

    ReplyDelete
    Replies
    1. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a .Net developer learn from Dot Net Training in Chennai. or learn thru ASP.NET Essential Training Online . Nowadays Dot Net has tons of job opportunities on various vertical industry.

      Delete
  2. 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.
    software testing training in chennai

    ReplyDelete
  3. I wondered upon your blog and wanted to say that I have really enjoyed reading your blog posts. Thanks for share to our vision..
    Selenium Training in Chennai | Software Testing Training in Chennai

    ReplyDelete
  4. I love this post.We share give the now amazing post.I understand all fantastic info.We will have shared learning it amazing post.Learn Python Online | Python Online Training

    ReplyDelete
  5. 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.
    selenium training in bangalore

    ReplyDelete
  6. Interesting and informative article.. very useful to me.. thanks for sharing your wonderful ideas.. please keep on updating..
    Best BE | B.Tech Project Center in Chennai | ME | M.Tech Project Center in Chennai | Best IT Project Center in Chennai

    ReplyDelete
  7. 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.
    Java Training Institute in Chennai | DotNet Training Institute in Chennai | Web Designing Training Institute in Chennai

    ReplyDelete
  8. 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

    ReplyDelete
  9. I 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

    ReplyDelete
  10. The 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

    ReplyDelete
  11. Your article is detailed, thanks to it I solved the problem I am entangled. I will regularly follow your writers and visit this site daily.
    Summer Courses for Business Administration in Chennai | Best Summer Courses in Porur

    ReplyDelete
  12. I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.

    Quicktest Professional Training

    ReplyDelete

Post a Comment

Popular posts from this blog

What is a PBI?

After my last post, I had the question of what is a PBI... so I thought i'd write a short blog post about what they are and why they are used.

A PBI is an acronym for Product Backlog Item. It is a description of a piece of work that your SCRUM team will develop and deliver. When you have a list of Product Backlog Items, you then refer to that collective list as a Product Backlog.

The product backlog is often prioritised and yourteam will work through each PBI, and release on a regular schedule... I am however going deep into the world of Agile development, which isn't entirely what this post is about, so I will stop myself now.

A Product Backlog Item is made up of the following:

Title - This is often a one liner that gives the team an idea of what the PBI is about, although it can just be an ID for the item and the team work off of that.

Description - Breaks down the PBI in a bit more detail, and can be written in any style, however I prefer it to be written as follows: 



By writin…

Dealing with Selenium WebDriver Driver.Quit crashes (Where chromedriver.exe is left open)

We recently came across a problem with Selenium not quitting the webdriver and this would then lock a file that was needed on the build server to run the builds.

We were using Driver.Quit() but this sometimes failed and would leave chromedriver.exe running. I looked around and found this was a common issue that many people were having. We (I say we, as we came to the solution through paired programming), came up with the following, that would encapsulate the driver.quit inside a task and if this task takes longer than 10 seconds, then it will clean up any processes started by the current process, in the case of the issue on the build server, it would kill any process started by Nunit.

[AfterTestRun]
        public static void AfterTestRun()
        {
            var nativeDriverQuit = Task.Factory.StartNew(() => Driver.Quit());
            if (!nativeDriverQuit.Wait(TimeSpan.FromSeconds(10)))
            {
                CleanUpProcessByInheritance();
            }
       }

        private s…

Famous Movie Quotes applied to Software Engineering - Jaws

You're gonna need a bigger boat? How can that relate to Engineering?

Firstly, let me ashamedly admit, that I've never seen the whole of Jaws all the way through. It's on my list of films to watch, but whether I get round to it, is another matter!



Anyway, to apply this to engineering, it's almost like "you're gonna need more testers/developers"...

We hear this all too often when trying to push releases out the door, let's throw men at it... However, as we all know, a bigger boat/more men... isn't always the answer, it's not a guarantee of quality, or even a guarantee of getting things done quicker.

If you have a task that will take 2 hours, simply having 2 people work on it doesn't mean that it is halved, in fact often, the time taken to do the task remains at 2 hours, but the maintainability and the knowledge around that area is increased, so it's a price, in my opinion that is often worth paying.