Sunny
Sunny

Reputation: 33

How to Record test execution using Selenium C# in .NetCore

I'm Trying to record test execution using Selenium, C#, MSTest in .NetCore

Tried using Microsoft.Express.Encoder nuget pkg, which throws error on creating instance of ScreenCaptureJob

 "System.BadImageFormatException: Could not load file or assembly 'Microsoft.Expression.Encoder, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. An attempt was made to load a program with an incorrect format." To resolve this, tried running test using x86, x64 and Any CPU, but none of them works.

Also tried using Nunit.Video.Recorder, with Nunit Framework, when did this, test are not discovered, tried changing to x86, x64 and Any CPU




1>C:\Users\Sunny\source\repos\ScreenRecorder\NUnitTestProject1\NUnitTestProject1.csproj : warning NU1701: Package 'Nunit.Video.Recorder 1.0.0' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.2'. This package may not be fully compatible with your project.
1>C:\Users\Sunny\source\repos\ScreenRecorder\NUnitTestProject1\NUnitTestProject1.csproj : warning NU1701: Package 'SharpAvi 2.1.0' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.2'. This package may not be fully compatible with your project.
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2106,5): warning MSB3270: There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "C:\Users\Sunny\.nuget\packages\nunit.video.recorder\1.0.0\lib\net452\NunitVideoRecorder.dll", "x86". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project.
1>NUnitTestProject1 -> C:\Users\Sunny\source\repos\ScreenRecorder\NUnitTestProject1\bin\Debug\netcoreapp2.2\NUnitTestProject1.dll
1>Done building project "NUnitTestProject1.csproj".
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

[8/9/2019 4:18:30.549 PM Informational] ---------- Run started ----------
[8/9/2019 4:18:31.686 PM Informational] NUnit Adapter 3.11.0.0: Test execution started
[8/9/2019 4:18:31.699 PM Informational] Running all tests in C:\Users\Sunny\source\repos\ScreenRecorder\NUnitTestProject1\bin\Debug\netcoreapp2.2\NUnitTestProject1.dll
[8/9/2019 4:18:31.806 PM Informational]    NUnit failed to load C:\Users\Sunny\source\repos\ScreenRecorder\NUnitTestProject1\bin\Debug\netcoreapp2.2\NUnitTestProject1.dll
[8/9/2019 4:18:31.807 PM Informational] NUnit Adapter 3.11.0.0: Test execution complete
[8/9/2019 4:18:31.810 PM Warning] No test matches the given testcase filter `FullyQualifiedName=Tests.Tests.Test1` in C:\Users\Sunny\source\repos\ScreenRecorder\NUnitTestProject1\bin\Debug\netcoreapp2.2\NUnitTestProject1.dll
[8/9/2019 4:18:31.936 PM Informational] ========== Run finished: 0 tests run (0:00:01.3376823) ==========

Upvotes: 1

Views: 4405

Answers (3)

mckrex
mckrex

Reputation: 45

I had the exact problem as the original poster and developed an additional solution. I think this solution integrates better than a batch file and gives finer control than the DataCollector in the accepted answer. I integrate a library called ScreenRecorderLib into my test projects to record test actions. Since the package is integrated into my project I don't need a separate batch file. I can also specify the window to be recorded, add overlays, and initialize, start, pause, and stop the recorder within the test execution. I have a sample GitHub project here. The ScreenRecorderLib project is very well documented. Here's a basic description how I use it in a Selenium test.

To record a test, first create a Recorder object:

var myRecorder = ScreenRecorderLib.Recorder.CreateRecorder()

Then, before recording anything, initialize the web driver and launch the browser window. ScreenRecorderLib can identify recordable windows with the method GetWindows() so after launch the browser under test will be present. GetWindows() lists the newest window first, so the window you want will likely be the first item in the array, but I define the window title so finding the window is more exact (in ChromeDriver, it's "--window-name=<some_unique_value>"). After finding the window, set it as one of the recording source options on the recorder.

var options = new ChromeOptions();
options.AddArgument($"--window-name=<some_unique_value>");
var driver = new ChromeDriver(AppDomain.CurrentDomain.BaseDirectory, options, TimeSpan.FromSeconds(300));
var allSources = new List<RecordingSourceBase>();
var testBrowser = ScreenRecorderLib.Recorder.GetWindows().FirstOrDefault(w => w.Title == "<some_uniques_value>");
myRecorder.SetOptions(new RecorderOptions
{
    SourceOptions = new SourceOptions { RecordingSources = sources }
});

The recorder needs a path for the video. This can be defined however you want. I use the test name and a time stamp in my projects.

myRecorder.Record(<path_to_recording>);

There are other options available, including options for overlays, audio, and encoder properties. You can also set options dynamically during the recording.

Three things to note. First, for whatever reason, ScreenRecorderLib won't work on a project with "Any CPU" as the platform; you must select something specific. I just pick x64 and don't worry more about it. Second, the recorder needs to be stopped gracefully to produce a valid video file. If the test quits because of an assertion failure, the driver will close and the recorder will quit before it can do so. For this reason, I always call the Stop() method inside of a test cleanup method so I can get a valid recording file and see what went wrong. Finally, the browser cannot be opened in "headless" mode. There needs to be a window.

Upvotes: 0

B Gupta
B Gupta

Reputation: 23

So I was going through the same trouble and manage to create a simple solution for .netcore selenium Nunit(mstest in your case) test. Microsoft encoder is deprecated, so i went for using a local screen recorder ffmpeg. just download the ffmpeg and start recording by calling this exe from your selenium C# test. Please know that this approach is good for only local execution. you may modify this to fit your other needs like selenium grid or remote executions.

Step 1:- Download ffmpeg in your local machine (https://ffmpeg.org/download.html) Follow this article to download (https://www.wikihow.com/Install-FFmpeg-on-Windows), please manually check once if your ffmpeg is working or not.

Step 2:- Create a bat file to start your ffmpeg (you will be calling this batfile from your c# code) Contents of Bat file

@echo off

mkdir "C:\ffmpeg\ffmpeg"

set outputpath="path to your recording output folder"\\record_20%date:~-2,2%_%date:~-10,2%_%date:~-7,2%_%time:~-11,2%%time:~-8,2%.mp4

ffmpeg -f gdigrab -framerate 30 -i desktop "%outputpath%"

Step 3:- In you selenium test , create a recording class and 2 methods to start and stop the recording(in my case I was starting the bat file before all test as in calling the executeScreenRecordingBatFile method in onetimesetup attribute to start the recording and calling the StopScreenRecording method in onetimeteardown ) Sample code below.

using System.Diagnostics;
using System.IO;

namespace FunctionalTests
{

    public class Recording
    {
        
        
        public static Process process;


        public static void executeScreenRecordingBatFile()
        {
            try
            {
                process = new Process();
                process.StartInfo.FileName = @"C:\Program Files (x86)\StartScreenRecording.bat";
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardInput = true;// this required to send input to the current process window.
                bool started =  process.Start();
                if (started==true) 
                {
                    Console.WriteLine("Bat file started");                                    
                    
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace.ToString());
                throw;
            }
        }

 public static void StopScreenRecording() 
        {
            StreamWriter myStreamWriter = process.StandardInput; // this required to send StandardInput stream, nothing fancy 
            myStreamWriter.WriteLine("q"); //this will send q as an input to the ffmpeg process window making it stop , please cross check in task manager once if the ffmpeg is still running or closed.

        }
       
}

Upvotes: 0

J.D. Cain
J.D. Cain

Reputation: 659

If you are in a windows environment with Visual Studio installed you can use a datacollector in the .runsettings file.

<DataCollector uri="datacollector://microsoft/VideoRecorder/1.0" assemblyQualifiedName="Microsoft.VisualStudio.TestTools.DataCollection.VideoRecorder.VideoRecorderDataCollector, Microsoft.VisualStudio.TestTools.DataCollection.VideoRecorder, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" friendlyName="Screen and Voice Recorder">
<!--Video data collector was introduced in Visual Studio 2017 version 15.5 -->
</DataCollector>

If you use the datacollector I suggest outputting a TRX file log from the test runner so that you can map what video goes with what test.

Other options include using Selenium Grid or implementing your own recorder call via something like ffmpeg using ffmpeg.net see this issue.

Here is a direct way to start and stop ffmpeg

    var si = new ProcessStartInfo
    {
        Arguments = "-y -f gdigrab -framerate 10 -video_size 1920x1080 -i desktop output.mp4",
        FileName = _fixture.FFmpegPath,
        RedirectStandardInput = true,            
    };

    var ffmpegProcess = Process.Start(si);
    await Task.Delay(15000);
    ffmpegProcess.StandardInput.Write("q");
    ffmpegProcess.WaitForExit();

Upvotes: 1

Related Questions