Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Wednesday, September 13, 2023

.Net Framework v/s .Net - One more performance comparison

Some of us might have heard why I should upgrade to .Net from legacy .Net Framework as it works and I want to run the application only on Windows servers. We as developers reply to them that .Net is modern and getting a lot of new features etc. Along with we emphasize that .Net is faster than .Net Framework. Should we use Linux to get that performance boost or will it be faster in Windows? There are a lot of comparisons¹ available on the net regarding the same. Here is one more comparison.

Application use case

This post is not to create a log of use cases and compare performance between the 2 frameworks. Rather this is comparing the encryption performance.
The application encrypts and decrypts data using X509Certificate. The use case tested may not be applicable in many projects but this is a solution to send data securely between the producers and one consumer. All the producers have the public key to encrypt and the consumer has the private key to decrypt.


This has projects that consume the shared code with minimal conditional compilations. Those are console apps and are ready to run with the required frameworks installed.

Please create an issue or give a PR if anyone finds problems in the test code.

Test environment

Model - HP EliteBook 840 G3
OS - Win 10 21H2 (OS Buid 19044.2194)
CPU - Intel i7-6600U @ 2.60Hz
RAM - 16GB
.Net Framework Version - 4.8(Runtime v4.0.30319)
.Net Version - .Net 6 (6.0.16)

Results

The .Net performs twice the speed of the .Net Framework. The picture is worth 1,000 words

The results were taken from an average of 3 attempts.

References


Tuesday, August 22, 2023

PowerShell to write XML with attributes on new line

Context

Many times after patch deployment of the application into the server there will be questions on what changed? The source code is obviously changed and the binaries can't be compared to find out what changed.

But the textual configuration files can easily be compared. Take a backup of existing files. Do the patching and compare what changed.

This post is assuming the deployment tools are using PowerShell to change the config files.

Problem

Look at the image below.

It shows there are a lot of changes done to the file. But really there is only one change. dbserver3->dbserver2

As we can easily understand the comparison considered the files as text, not as XML, and found a lot of differences.

Approaches

If the number of files is less this is fine but that is not the case in enterprise. There are multiple approaches to this problem.

A tool that understands XML and compares

The above comparison tool is not recognizing the XML format instead it compared textually. Find another tool that understands the XML format and compare accordingly.

To date, we didn't find any such tool. Please comment if there are tools that are free and export the comparison report.

Always have attributes on a new line

In this approach make sure all the tools and programs that modify the XML write attributes on a new line.
Since PowerShell is running on top of .Net it is easy to get anything working in PowerShell that works in .Net. Google easily lands us in pages that say use the XMLWriterSettings.NewLineOnAttributes to get the job done. Unfortunately, it is not working out of the box.

But when we combine the .Indent property with .NewLineOnAttributes, it works fine.

[XML]$doc = get-content ".\input.xml"
$xwSettings = new-object System.Xml.XmlWriterSettings
$xwSettings.Indent = $true
$xwSettings.NewLineOnAttributes = $true
$xmlWriter = [Xml.XmlWriter]::Create("output.xml", $xwSettings)
$doc.Save($xmlWriter)
$xmlWriter.Dispose()

The above code works. Now the question is, is this documented? Oh yes in the remarks section.
Of course, the default value of the Indent property is false. The comparison looks as below after using the above code.


Happy formatting.

Tuesday, January 24, 2023

Freemium Mock Web APIs for testing - Beeceptor

Often when we develop we will end up in situations where we need to simulate the real scenario by mocking the dependencies. It may be databases, Web APIs, or container images. There was a separate post on mock container images that echo HTTP. 

Problem

There are scenarios where we may not be having the opportunity to run containers to expose the mock Web APIs. This post about handling such scenarios

Solutions

There are many solutions to address this.

Hosting Web API from our machine

We don't need to code from scratch. Just search in GitHub and we can get many. Simply clone and run.

Using internet-hosted mock Web APIs

This works if our development environment has access to the internet. There are paid as well as freemium services. One such freemium service is Beeceptor. 

Please note this post is not sponsored by Beeceptor also the author is not at all affiliated to the service. Their service saved some time today hence this post.

Some features are below
  • The free tier gives 50 requests per day. 
  • Integrated with Google and GitHub accounts. No need to remember one more password.
  • Support delay even in the free tier.
  • Support custom response headers and status codes.
  • The HTTP responses can be configured quickly using the 'Mocking Rules' editor.

This is not a sophisticated mock Web API provider but it helps us to get quick mocks

Happy mocking...

Tuesday, March 10, 2020

Code coverage for .Net multi-targeting library

Background

When we develop an open-source project in GitHub, it is a good practice to show how perfect we are doing things through badges. One of the measures is code coverage. Code coverage means how deep our code is tested. 
This is a post to discuss how to set up code coverage for a multi-targeting .Net library by analyzing how it is done for DotNet.Helpers nuget library. Below is the live badge of DotNet.Helpers.


Test frameworks

There are multiple choices here. The best way is to go with the native MSTest.

Coverage file formats

There are different formats to store the code coverage. OpenCover is a stable format that uses XML to store the results.

Tools

OpenCover

This tool support .Net Core natively. It has 1.2k GitHub stars as of writing this post. The problem with the tool is that we cannot generate the coverage report using dotnet cli. Also, it supports only Windows. Is that a problem in the .Net world. Maybe not now, if we are using CI/CD provider with Windows. But it will be a consideration when Microsoft stops Windows and asks all of us to use Linux. Their current direction is 'Develop anywhere, use any tool but host in Azure'.

//This doesn't seem working
dotnet test with --collect: "Code Coverage"

Coverlet

This is another great tool to get the results. It can be integrated with the .Net build system. Coverlet doesn't merge results automatically when we are using the multi-targeting library. Though it defeats the purpose, we can work around using different methods.

The main reason for using Coverlet is that it works natively with dotnet test command. We will see the syntax later in this post.

Coverage visualizers

Generating a file is not enough. We need somebody who can track coverage and display beautiful reports. Often hosted services with freemium pricing model.

CodeCov v/s CoverAlls

Both CodeCov and CoverAlls good to use. In case anyone needs to do research, below are the links.
https://stackshare.io/stackups/codecov-vs-coveralls
https://www.appveyor.com/blog/2017/03/17/codecov/

DotNet.Helpers library uses CodeCov for tracking coverage.

How DotNet.Helpers implemented code coverage

It has 3 steps

Setting up the coverage generator tool

This step is a one-time activity. It installs coverlet.msbuild nuget package

Install-Package coverlet.msbuild

Please make sure the package is installed on the test project having the test classes.

Generate coverage report

This step has to happen with each build. The step is mentioned in the appveyor.yml. Similar to AppVeyor, it can be configured with any CI/CD provider. Below is the simple command to generate the report.

dotnet test "DotNet.Helpers.Tests\DotNet.Helpers.Tests.csproj" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
/p:Threshold=100 /p:ThresholdType=line --configuration Debug --no-build

The above command should be executed as a single unit. Multiple lines are used in this post for easier understanding.
The switch /p:Threshold=100 means it requires 100% code coverage and /p:ThresholdType=line tells the coverage is at the line level. Meaning all the lines in the library should be executed at least once during the unit testing.

The command will execute tests against all the framework versions mentioned in the test project (.csproj) file. But overwrites instead of merging as we didn't mention the target version in the output file name.

Upload coverage report

Uploading to service is optional but it helps to show the badge on how good our library is tested also reports. DotNet.Helpers library uses CodeCov service. Uploading the report to CodeCov can be done using the below command.

choco install codecov --no-progress
codecov.exe -f "DotNet.Helpers.Tests\coverage.opencover.xml" -t $env:CODECOV_TOKEN

The choco is the command to install software using Chocolatey. The $env:CODECOV_TOKEN is optional. CodeCov can check whether it is running as part of the CI/CD process inside AppVeyor and if the criteria satisfy, it can magically upload without the token.

Adding Badge

In GitHub readme.md we can embed the badge code as below.
[![codecov](https://codecov.io/gh/joymon/dotnet-helpers/branch/master/graph/badge.svg)](https://codecov.io/gh/joymon/dotnet-helpers)

Below is the badge code in HTML to embed in any web site
<a href="https://codecov.io/gh/joymon/dotnet-helpers">
  <img src="https://codecov.io/gh/joymon/dotnet-helpers/branch/master/graph/badge.svg" />
</a>

Source of DotNet.Helpers can be found in GitHub. Happy testing...

Tuesday, February 18, 2020

Complex .Net unit testing of WinForms BeginInvoke()

This post is about writing unit tests for Winforms when there is threading involved.

Requirement

The nuget package DotNet.Helpers contain an API ISynchronizeInvokeExtensions which has an extension method InvokeIfRequired(). That provides the feature to invoke code that can manipulate UI controls from a different thread than it was created.

When unit testing the API, the test code has to execute using a properly threaded test.

Expected solution

It could seem very simple as below. Create a control in the current thread and access it from other thread.

[TestMethod]
public void WhenCalledFromOtherThread_ShouldUseBeginInvoke()
{
            Control ctrl = new Form();
            ctrl.Enabled = false;
            int threadIdWhereControlCreated = Thread.CurrentThread.ManagedThreadId;
            int threadIdWhereControlModified = -1;
            Task task = Task.Run(() =>
              {
                threadIdWhereControlModified = Thread.CurrentThread.ManagedThreadId;
                  ctrl.InvokeIfRequired(() =>
                  {
                      ctrl.Enabled = true;
                      ctrl.Text = "from therad";
                  });
              });
            task.Wait();        Assert.IsTrue(threadIdWhereControlCreated != threadIdWhereControlModified && ctrl.Enabled);
}

But it didn't work. Meaning InvokeIfRequired() didn't call the BeginInvoke(). Hence didn't get 100% coverage.

Working solution

Below is the working version.

    [TestClass]
    public class ISynchronizeInvokeExtensions_InvokeIfRequired
    {
        [TestMethod]
        public void WhenCalledFromOtherThread_ShouldUseBeginInvoke()
        {
            bool finished = false;
            TestForm testForm = new TestForm();
            testForm.Show();
            testForm.Finish += (sender, args) =>
            {
                testForm.InvokeIfRequired(() =>
                {
                    finished = true;
                    testForm.Close();
                });
            };
            testForm.Text = "trigger";
            while (!finished)
            {
                Application.DoEvents();
                Thread.Yield();
            }
            Assert.IsTrue(finished);
        }
    }
    public partial class TestForm : Form
    {
        public event EventHandler Finish;
        public TestForm()
        {
            this.TextChanged += (sender, args) =>
            {
                Thread runner = new Thread(() =>
                {
                    if (Finish != null)
                        Finish(this, EventArgs.Empty);
                });
                runner.Start();
            };
        }
    }

As seen in the code, the form is opened using the form.Show(). If that line is not there, the BeginInvoke will not get called.

Note: It works in the local development machine using Visual Studio and AppVeyor during CI/CD process. If the CI/CD tool selected doesn't support showing a form the unit test will not work.

Sample

Working code can be found at
https://github.com/joymon/dotnet-helpers/blob/master/DotNet.Helpers.Tests/WinForms/ISynchronizeInvokeExtensions_InvokeIfRequired.cs


Happy unit testing...

Tuesday, February 4, 2020

Failing AppVeyor build on low test coverage in .Net application using Coverlet

Introduction

Normally this scenario may seem straight forward. In fact, t is very simple. We need to fail a .Net build in CI/CD process if the test coverage is less than a defined threshold. We are using Coverlet for generating the test coverage reports and AppVeyor for CI/CD.

The command to generate reports and error out the process is very simple as below.

dotnet test "<target>" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover 
/p:Threshold=100 /p:ThresholdType=line 

<target> is the path to solution or project file.

Problem

The command is self explainable. There are parameters to mention the Threshold and the ThresholdType. It errors out when we run locally. But when the same run from AppVeyor, it just shows the error message and continues to succeed.

One such sample can be found in the below link.
https://ci.appveyor.com/project/joymon/dotnet-helpers/builds/30446410#L79

It just says there is an error but continues.

C:\Users\appveyor\.nuget\packages\coverlet.msbuild\2.2.1\build\netstandard2.0\coverlet.msbuild.targets(23,5): error : 'DotNet.Helpers' has a line coverage '92.9%' below specified threshold '100%' [C:\projects\dotnet-helpers\DotNet.Helpers.Tests\DotNet.Helpers.Tests.csproj]

Troubleshooting

There were no direct google results found to solve the issue. So created the below question in the AppVeyor forum.

https://help.appveyor.com/discussions/problems/26109-net-build-is-not-failing-when-the-test-coverage-is-low-using-coverlet

But later when searched for similar issues in other CI/CD platforms, one question got the attention.

https://stackoverflow.com/questions/56631066/azure-devops-build-dont-fail-when-coverage-is-below-target-with-dotnet-test

As per the answer in this post, the action has to be performed differently to fail the build. It feels something custom. So decided to fail the AppVeyor build by code based on the result from dotnet test command. 

Solution

Not sure its actual solution or a hack to fail the build as the command is not failing the build. Below goes the solution.

dotnet test "<target>" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover 
/p:Threshold=100 /p:ThresholdType=line

if($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode ) }

It is simple as checking the $LastExitCode and setting it to the $host to indicate the runtime that it failed. Normally any non zero result code is considered as a failure.

Working sample

Full working AppVeyor.yml can be viewed in the below GitHub repo

https://github.com/joymon/dotnet-helpers

A build log showing it failed due to code coverage can be found here.

Happy debugging...

Tuesday, December 3, 2019

Linux Container not allowing to install packages - Selenium network debugging

It started with someone who needed to containerize Selenium. There is standard help available on how to do this. But less when something goes wrong. One such scenario is the network connectivity. In our enterprise environment, connectivity is a mystery and it played a villain role here too.

Selenium providing the debug version of the container image. We tried that to include the ping utility as ping is not part of the standard Ubuntu package.

Below is the Dockerfile we tried to install network utilities into the Selenium image


FROM selenium/standalone-chrome-debug:3.141.59-xenon
RUN apt-get update && apt-get install -y iputils-ping
RUN apt-get install dnsutils
EXPOSE 4444
EXPOSE 5900

Didn't worked. It was complaining about the permission to install. We never used any user name and password while doing anything in this exercise. So confused on how to give permissions. Also, we are mainly a Windows shop. Fewer experts in Linux.

Finally, google helped as usual. We switched to root by using the USER keyword in Dockerfile and get the utilities installed.

FROM selenium/standalone-chrome-debug:3.141.59-xenon
USER root
RUN apt-get update && apt-get install -y iputils-ping
RUN apt-get install dnsutils
EXPOSE 4444
EXPOSE 5900

The beauty of containers is that only one person has to struggle. The Selenium image with network utilities can be obtained from the below location.

https://hub.docker.com/r/joymon/selenium-standalone-chrome-debug-network

For a person from the Linux world working with containers for long, this may not be big news:)

References

https://github.com/WASdev/ci.docker/issues/194
https://github.com/SeleniumHQ/docker-selenium/issues/725

Tuesday, April 9, 2019

Visual Studio Online / Azure DevOps load testing - On-premise agent service is not starting

This is a simple troubleshooting post related to VisualStudio Online or Azure DevOps load testing.
The load testing needs agent programs to be installed on multiple machines to generate load. We can use agents provided by Microsoft as well if our application is straight forward and accessible from Microsoft hosted agents.

If we are in Enterprise or in restricted environments, we may have application hosted internally and only accessible via domain joined machines or via other constraints. Then we need to run the agent program on premise.

Problem

The agent program is a windows services which we can see in services.msc console. Sometimes the agent service gets stopped and below error will show up when we try to restart.

---------------------------
Services
---------------------------
The VSTSLoadAgentService service on Local Computer started and then stopped. Some services stop automatically if they are not in use by other services or programs.
---------------------------
OK 
---------------------------

Solution

Since this Azure DevOps load testing tool is deprecated now and ending its life next year around this time, lets not go into the details why its not working. Lets see a hack.

As standard Microsoft solution which is as famous as machine restart, lets uninstall and install the agent program again and it will work.

Since the installation is by running below PowerShell scripts, it doesn't seems polluting registry and all.

Uninstalling agent

Uninstall command needs the agent name. If the name if unknown, first we need to get agents list. Example is as follows.

.ManageVSTSCloudLoadAgent.ps1 -TeamServicesAccountName https://<organization>.visualstudio.com -PATToken <PATToken> -AgentGroupName <group name> -GetAgents

to uninstall an agent follow the below command.

.ManageVSTSCloudLoadAgent.ps1 -TeamServicesAccountName https://<organization>.visualstudio.com -PATToken <PATToken> -DeleteAgent -AgentGroupName <group name> -AgentName <agent name>

Now install the agent again.

.ManageVSTSCloudLoadAgent.ps1 -TeamServicesAccountName https://<organization>.visualstudio.com -PATToken <PATToken> -ConfigureAgent -AgentGroupName <group name>

The ManageVSTSCloudLoadAgent.ps1 file can be downloaded from this location. The commands above has some parameters enclosed in '<' & '>' as placeholders. We need to use actual values there.

Tuesday, November 20, 2018

Detecting whether the code is running under Karma test runner

Disclaimer

Putting the disclaimer in the beginning as it is a bad practice to check whether our code is running under the test and do actions based on it. Ideally it should be done via dependency injection and mock objects.

But still to document there is an option to check whether the code is running under test and trying to explain why its needed in one scenario.

How to check code is running by Karma

The idea came from the below SO question.

https://stackoverflow.com/questions/26761928/how-to-check-if-you-are-using-karma

Before going into the code let us take some time to understand Karma. It is a test runner. It runs the JavaScript application by starting browser instance(s). Those instances can be either with UI or headless. Headless means no UI or consider as in memory.

Lets understand the code

function isKarmaRunning(){
  let isKarmaRunning = false;
  if (typeof window["__karma__"] !== "undefined") {
    isKarmaRunning = true;
  }
  console.log(`[bootstrap] isKarmaRunning - ${isKarmaRunning} ${typeof window["__karma__"]}`); // still undefined
  return isKarmaRunning;  
}

It is self explanatory and show the danger itself. It assumes when the code is run by Karma test runner, the window object will get a properly called __karma__. We can detect the presence of Karma by checking that property.

The danger is that in future when Karma decided to stop setting the __karma__ property or change its name, our code will fail.

Thanks to laukok for his question with solution.

One scenario to detect whether Karma is running

This is not a scenario where we must check for Karma. Rather its hack and you are free to suggest how to do it right.

The scenario is where AngularJS and PWA (Progressive Web Apps) are combined. PWA's ServiceWorker feature is used to intercept the web requests to populate cache or serve from cache instead of hitting server. The cache is filled to ensure that the application can be used even without internet connection.

The ServiceWorker has fetch API which is used to intercept the web requests. fetch API hook starts only after the service worker is properly initiated. It may take up to 100ms as per different sources. If AngularJS application is bootstraped before ServiceWorker comes into active mode, it will not catch the web requests hence not cached. The web requests include the requests from ng to get the html views

If the user is still online and use the application, there are chances that those web requests are again intercepted and stored into cache. But what if the user first launch the application and went to offline. The cache is not populated hence error.

In order to work the initialization delay one of the solutions is to explicitly bootstrap the Angular application once the ServierWorker has started. Then the problem is that the tests written will complain that the Angular application is not bootstraped. Then either we need to change all the tests to wait for the ServiceWorker to initialize or have the application changes to do below

If the application is running under Karma, boo Angular immediately. Else wait for the ServiceWorker to initialize.

Too complicated. Isn't it. As an software engineer we should have gone back to original problem and solved the issue by separating the components and directives to separate ng module or one of the other approaches. 

Hopefully once there is enough time to analyze there will be a follow up post with better solution. In mean time feel free to pour your comments.



Tuesday, May 16, 2017

Protractor - CI with AppVeyor issue on webdriver-manager update

Protractor is a nice tool to do testing of Angular applications. It really shines when used in continuous delivery scenarios because of its end to end testing capabilities. We can simply host the site and run our tests with Protractor. But when we use it with hosted CI solutions there is a chance for getting the below exception in the build process.

(node:1184) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Failed to make Github request, rate limit reached.

This happens when we issue the below command which we cannot avoid to get it working.

webdriver-manager update

One such failure can be seen here. What is really happening is that during the update, the webdriver-manager is trying to download something from GitHub for Gecko driver. Since most of the hosted CI solutions use same single IP to access outside internet, the GitHub API limits comes into play.

This is already known issue and the only way is to disable Gecko driver for now. Command to disable Gecko driver is as follows.

webdriver-manager update --gecko false

Thanks to the AppVeyor support team.

Tuesday, March 1, 2016

npm install karma in windows 8 ends with error TRK0005: Failed to locate: "CL.exe"

I was recently working with Travis-CI to setup CI & CD for a pure browser app/static web site. Details are there in a previous post. Karma is the one decided to use as test runner. Things were good till the time I start installing Karma into local node folder using --user-dev. The error I got is as follows when installing Karma.

Building the projects in this solution one at a time. To enable parallel build,
please add the "/m" switch.
TRACKER : error TRK0005: Failed to locate: "CL.exe". The system cannot find the
 file specified. [C:\Users\<user name>\AppData\Roaming\npm\node_modules\karma\node_mod
ules\bufferutil\build\bufferutil.vcxproj]

gyp ERR! build error
gyp ERR! stack Error: `C:\Program Files (x86)\MSBuild\14.0\bin\msbuild.exe` fail
ed with exit code: 1

It turns out though it says NPM module its using lot of C++ inside. Karma cannot be built without C++ compiler. When I googled most of the people are saying it never tested in Windows machines. I was not in a situation to go to a Linux machine for this. After spending more time with google, I could see that it requires VS 2015 with Visual C++ language support.

Reinstalled VS 2015 with C++ features and it worked.

Moral of the story - Open source is still mainly for Linux developers :)

Tuesday, February 16, 2016

CI & CD for pure HTML+JS browser apps using Travis-CI

When its said pure HTML+JS browser app, it really mean. There is no web services or server side HTML generation involved. Its simple HTML+JS site running inside browser. People may wonder why such an browser app needs continuous integration where there is no compilation involved.

That's true that it doesn't need compilation. But it always can have tests and those tests can be run after each git push and deploy to staging or production. Many of the enterprise people will not have such a scenario of pure client app. However lets look how can we setup Continuous Integration & Continuous Delivery for such an pure HTML+JS SPA.

Objectives

  • Upon git push the integration process should start
  • It should unit test the tests written using Jasmine framework.
  • If the tests are working, deploy to a FTP location in a web site.

Tools & Frameworks used

The app uses HTML5,JS and jQuery. Below is the list used for CI & CD activities.
  • Github - where the code is stored
  • Travis-CI - which provides machine to run the CI & CD activities
  • Jasmine - Test Framework API
  • Karma - Test runner
  • PhantonJS - The headless browser where Karma can run the tests
  • NodeJS
  • NPM Modules other than required by above
    • ftp-deploy

Node.JS

Do not confuse with the usage of Node.JS. We are not going to run anything in server, its pure HTML+JS SPA. NodeJS is just to do npm package management and to upload to FTP for deployment. The test runner Karma which we are going to use comes as npm package. Also this is the language type supported in Travis-CI for pure HTML+CSS+JavaScript projects.

Prerequisites

This assumes the understanding of
  • Basic web development using HTML+JS+CSS (browser side alone)
  • Using Github.
  • Understanding about NodeJS and its package management ecosystem npm.

Setting up source - production & test code

Application

The application which we are going to use is HTML5 based Karel simulator. Instead of creating separate branch inside it's git repository, better we can create another repository named travis-ci-test in Github itself and uploaded the same source there. /Src is the folder which contains source code.

Tests

There is no question that the leader in client side web testing is Jasmine framework. This provides the API to write test code. Since this is not a post to discuss about Jasmine framework, lets go to Jasmine test code directly. The test JavaScript files can reside inside in the /Tests folder.

Running tests using SpecRunner.html

Jasmine is a JavaScript test framework written in JavaScript and run tests inside browser. For that we just need to create a html file and refer Jasmine js files, app/production js files and test scripts. In this case a SpecRunner.html is available in below location. Browsing it will run the tests and show the results.


Make sure the tests are running from SpecRunner.html. It is always recommended to do a big job in small tasks which can be done in less than 1 hour.

Setting up NodeJS & NPM

The above setup will work really great in our machine. But when in CI environment, we need to run the tests from program. Travis-CI doesn't have client side only JavaScript+HTML app support. But it has node_js support which we can leverage here to test our app.

Next step is to add some Node.JS things. Basically we need to convert our folder to Node.JS application. We can create/convert a Node.JS app by simply issuing 'npm init' command, from any command prompt inside our folder. It will ask for some questions and will create package.json file.

Introducing Karma & PhantomJS

Once our folder is npm enabled or in other sense we got a package.json, we can install packages. Karma is a test runner program which can run our Jasmine tests we wrote in previous step. The JavaScript needs a engine to execute the script and Karma is not a js engine. So it needs a js engine. It can either use any browser itself or can use headless browser called PhantomJS. PhantomJS comes as npm package. All the packages mentioned till here can be installed via npm. See the devDependencies section in package.json file for the packages what we need.

In our local machine we can use npm install <package name> --user-dev command to install the packages. But in CI machine, it will do automatically by reading the package.json.

Setting up Karma & PhantomJS

Karma wants to know where are the production & test js files to run tests. For that it needs a config file. In this case, its  karma.config.js


This file can be created using karma init command. It will ask some questions and create the file. This file is self explanatory.

How npm knows Karma is the test runner to be used

Once the setup is done we are going to execute the tests by issuing below command.

npm test

How does npm knows to run the tests using Karma? Its again configuration inside package.json.

There is a section called 'Scripts' and it contains 'test' section inside. Test node expects an executable command. There we can give the command to run Karma along with karma config we created in previous step.

Once this step is proper we should be able to issue 'npm test' command and it should should run the tests and show results. Testing every step is important.

Upload to FTP via ftp-deploy npm module

Now comes to deployment. If all the tests are passed, we can deploy to our staging or production environment. In this case I want to deploy to a FTP location. The hosted CI platform Travis-CI which we are going to use, doesn't have direct support to deploy a folder to FTP. We had to rely on curl command which can transfer one file at a time. One file at a time doesn't work most of the time.

Now there are 2 ways
  • Use tools such as grunt / gulp which can orchestrate the CI activities. They have FTP deploy methods.(Google knowledge. Didn't try myself)
  • We can write some code in shell script or NodeJS and hook it after the tests run successfully.
As there are already enough tools and technologies just for CI & CD than the production code, lets write some custom NodeJS code to upload files to FTP. No to Grunt and Gulp for now.

The written FTP upload code uses ftp-deploy npm package to upload the files. Why should we rewrite code again for reading the folders, reading the files and to upload using FTP protocol. That is something many people already solved and made open source.

The FTP code can be hooked to the integration process after test by editing the package.json file. We can use 'posttest' section for it.

Testing everything in local machine

If the above environment is ready, we should be able to run everything in our machine. The command 'npm test' should run the unit tests and if everything pass, deploy to FTP folder. In our local environment to work we need to give the FTP credentials to ftpupload script via environment variables. We will see why its via environment variables in next section.

Intro to Travis-CI and getting started with it

Travis-CI is a free hosted CI&CD SaaS environment for open source projects. This is similar to AppVeyor. We could have used AppVeyor which is already referred in previous posts and have already used in other .Net projects. But this time lets try something new. Below are the steps to achieve the same.

  • Upload the code to Github.
  • Create account in Travis-CI using Github.
  • Connect to Github project.
  • Setup below environment variables for FTP access.
    • ftp_host
    • ftp_user
    • ftp_password - Do not turn the flag to display in build log. Build log is public
    • ftp_localPath - Path to the folder inside the CI machine. Usually src
    • ftp_remotePath - Path to where the files should be copied. Only relative url
Refer a build log for sample values. Once this is working. Just do a push to Github to start the integration process.

UI testing in Travis-CI

Travis-CI don't support running in chrome out of the box. They are telling we need to give some commands to get it running. To me it didn't work out. It tried 2-3 times and said failed to start Chrome. So decided to use PhantomJS. Anyway in this sample application, the main objective is test the JavaScript. So it works.

If there are any steps missing and difficult to follow just fork / download the sample. There is no good documentation than the code itself.

References

http://orizens.com/wp/topics/first-steps-in-setting-up-travis-ci-to-your-javascript-project/
https://blog.logentries.com/2015/01/unit-testing-with-karma-and-jasmine-for-angularjs/
http://swizec.com/blog/how-to-run-javascript-tests-in-chrome-on-travis/swizec/6647
http://gis.utah.gov/how-to-wire-up-travis-ci-to-your-js-projects/
http://www.sitepoint.com/testing-javascript-jasmine-travis-karma/

Tuesday, May 26, 2015

Moq - How to know the number of times a method is called on mocked object

Moq seems a good mocking library for .Net unit tests. It has most of the features a developer needs and pretty much stable and many people are using it. Once I started using it, I never felt a need to another mocking library. Below is one if the scenario which I had to face related to Moq.

Scenario

There is a method which randomly executes operations (IOperation implemented classes) stored in an array. The operations are unit tested independently to make sure there is no problems with them. This method which is in test simply uses Random class to get random number and get an operation from array using that number and invokes the operation.

Ideally speaking we don't need to test the Random class which is part of .Net framework. But why should we avoid unit testing our logic inside the method ? Some tests like 'making sure random value is not above the length of array' etc... So whats the big deal?

Challenge

The unit tests works by Arrange,Act,Assert methodology. First we need to arrange the things, Then act or do the operation by calling the method under test. Finally assert.

When we do the act, we should be knowing what is the output. Then only we can write assert. If we don't know what is going to happen we don't know what to assert.

An Example

Recently I forked ChaosMonkey for .Net and started contributing to it. I made it configurable. Looking into that code might be useful to understand the example better.It contains a MonkeyKeeper and IMonkey interface. MonkeyKeeper is the external interface. When MonkeyKeeper.Unleash() is called it should randomly pick an IMonkey from its monkeys collection and call Unleash() method on it. IMonkey implementations can be easily unit tested to make sure they are doing their duties on Unleash. But how to unit test MonkeyKeeper.Unleash()?

In this scenario, we can assert that the mocked IMonkey objects at least got one call to unleash. But in order to get at least one random calls, we need to call the MonkeyKeeper.Unleash multiple times because Random can return same value multiple times.

Below is one implementation, where MonkeyKeeper.Unleash() is called 5 times which is holding 3 IMonkeys collection.

[TestMethod]
        public void WhenMonkeyListProviderGivesMultipleMonkeysAndUnleashMultipleTimes_UseThoseMonkeysAtleastOnce()
        {
            Settings settings = new Settings();
            ChaosLogger logger = new ChaosLogger("");
        //Arrange
            var mockMonkey1 = new Mock<ParentMonkey>(settings, logger);
            var mockMonkey2 = new Mock<ParentMonkey>(settings, logger);
            var mockMonkey3 = new Mock<ParentMonkey>(settings, logger);
 
            var mockMonkeyListBuilder = new Mock<MonkeyListBuilder>();
 
            mockMonkeyListBuilder.Setup(builder => builder.GetMonkeys(settings, logger))
                    .Returns(new List<ParentMonkey>() {
                        mockMonkey1.Object,
                        mockMonkey2.Object,
                        mockMonkey3.Object
                    });
 
            MonkeyKeeper keeper = new MonkeyKeeper(
                settings,
                logger,
                mockMonkeyListBuilder.Object);
            //Act
            keeper.UnleashRandomMonkey();
            keeper.UnleashRandomMonkey();
            keeper.UnleashRandomMonkey();
            keeper.UnleashRandomMonkey();
            keeper.UnleashRandomMonkey();
            //Assert
            mockMonkey1.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
            mockMonkey2.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
            mockMonkey3.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
        }

Most of the times this test will succeed. But there is no guarantee that it will succeed always because Randon.Next() using inside MonkeyKeeper.Unleash can return same value 4 times and that's why its random. in such scenarios this test will fail. So whats the solution.

Solution

There is no solution to this. If we can predict random number and write asserts, that is not random. So what we can do?

At least make sure, there is some level of randomness in the selection of IMonkeys. Also we can make sure if there are 3 IMonkeys in the collection at least one of the IMonkey's unleash is called. If that is not called or called multiple times, there are some problems in the random selection logic of MonkeyKeeper.Unleash. Whats the problem in doing that?

We have 3 monkeys and we don't know which one will be selected by random logic. But we need to make sure that the unleash is called only once and on one monkey. This clearly tells that normal mechanism of asserting via Verify() and Times will not work. If there is any confusion just try writing that as below

mockMonkey1.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
mockMonkey2.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);
mockMonkey3.Verify(monkey => monkey.Unleash(), Times.AtLeastOnce);

Why the above wont work? when we unleash it may call mockMonkey1. and that line works and test passed. But what about the next lines? They will fail. We cannot put a return statement because next time mockMonket3 might be the one selected. In that case first 2 statements will fail.

Now whats the method? We need to know the number of times a method is called on mocked object. There is no direct way to know that. So we need to use the callback mechanism available in Moq to achieve our aim. Callback function helps to get a hook in our unit test when a particular method is called on mock object. Code goes as below.
[TestMethod]
public void WhenMonkeyListProviderGives3MonkeysAndUnleashOnce_UnleashMustBeCalledOnOneMonkey()
{
    Settings settings = new Settings();
    ChaosLogger logger = new ChaosLogger("");
    //Arrange
    var mockMonkey1 = new Mock<ParentMonkey>(settings, logger);
    var mockMonkey2 = new Mock<ParentMonkey>(settings, logger);
    var mockMonkey3 = new Mock<ParentMonkey>(settings, logger);
 
    int callCount = 0;
    mockMonkey1.Setup(monkey => monkey.Unleash()).Callback(() =>callCount+=1);
    mockMonkey2.Setup(monkey => monkey.Unleash()).Callback(() => callCount += 1);
    mockMonkey3.Setup(monkey => monkey.Unleash()).Callback(() => callCount += 1);
 
    var mockMonkeyListBuilder = new Mock<MonkeyListBuilder>();
    mockMonkeyListBuilder.Setup(builder => builder.GetMonkeys(settings, logger))
            .Returns(new List<ParentMonkey>() {
                mockMonkey1.Object,
                mockMonkey2.Object,
                mockMonkey3.Object
            });
 
    MonkeyKeeper keeper = new MonkeyKeeper(
        settings,
        logger,
        mockMonkeyListBuilder.Object);
    //Act
    keeper.UnleashRandomMonkey();
    //Assert
    Assert.AreEqual(1, callCount, "Unleash Called multiple times");
}

http://stackoverflow.com/questions/16693078/when-using-moq-verify-method-invocation-count-have-failing-tests-error-messa

Tuesday, May 19, 2015

Unit testing ASP.Net Web Forms

Scenario

We have a simple legacy web application created using ASP.Net Web Forms. It works well and doing its duties without fail. Mainly it deals with simple pages not many transactions involving many pages. But rarely there are changes coming in. Even if the changes come frequently, the changes are small changes which are not affecting the core functionality of web site. Being a passionate software engineer, we wanted to bring innovation into the project such as refactoring to patterns and more object orientation.

Problem

We are confident on the refactoring we are doing in the site. But still there is a factor of fear that whether those changes will break the other pages in the site or not? Basically need to make sure all the links are working. Normally after a change we manually click on every link and make sure they are all loading.

That is a repetitive work. No software engineer should be doing repetitive work. We need to solve this in less cost. As usual since its legacy application, there is no budget to buy tools or automated testing service.

Approaches

1.Write a tool to validate links

We can write a tool which will parse the html of default.aspx and make sure all the links are working properly ie returning valid html contents. Can use XMLHttpRequest or WebClient class to accomplish it. Can configure the tool to run as post build action.

Challenges are in validation because there is no guarantee that HTML pages can be easily parsed with XML classes. There is a nuget called HTML Agility pack which we can try here.

2.Write unit tests on existing Web Forms app

Write unit tests for all the pages and at least make sure all the are passing.

After evaluating, I feel the unit tests are the way to do developer testing. But is my legacy application written as testable. Absolutely no. Now what we can do? One way is to rewrite as ASP.Net or use the Url based unit test. Refactoring the business logic and testing those is not applicable as the site doesn't have complex logic and more than that we need to make sure the pages are all working after our change.

3.Convert into ASP.Net MVC and unit test

Though this is the perfect solution most of the time this will not work out because.
  1. There may not be enough budget for maintaining legacy app or convert into ASP.Net MVC because from the business perspective, the site is doing its duty properly
  2. There might be links already bookmarked by users. Those will be invalid unless there is a rerouting mechanism in new MVC application.

Solution - Unit test ASP.Net Web Forms

The concept of unit test is applied to one level above the normal unit testing of classes and methods. Here we test the ASP.Net Pages itself. We can check particular properties of Page is set when rendered for a particular request url. Below are the steps to create unit tests for ASP.Net Web pages

1.Create UnitTest Project

Create normal unit test project from VisualStudio->New->Project dialog which we used to create for other scenarios such as class libraries or MVC.

2.Refer System.Web

Reference to Syste.Web.dll in UnitTest project is required if we want to hold the Page variable for assert.

3.Write test method with special attributes

Now comes the testmethod same as in normal test project. Here additions are 2 attributes HostType and UrlToTest. They specifies where the web page is hosted and what page corresponding test method is going to test. Below is one scenario where test method is testing a page hosted in Local IIS server.

string url = "http://localhost/joymononline/"
[TestMethod]
[HostType("ASP.NET")]
[UrlToTest(baseUrl+"links.aspx")]
public void WhenLinksPageIsRequested_ShouldReturn200AndContent()
{
    Page page = TestContext.RequestedPage;
    string url = Path.Combine(baseUrl, "links.aspx");
    //Assert.IsTrue(UrlIsValid(url));
    Assert.IsTrue(page.Title.Equals("Joymon Online | Links"));
}

If you are looking for how to test a web application running from development server, please have a look at the below links.


I strongly recommend developing by pointing to local IIS server, because IIS is the way your application is going to be served to end users. Otherwise there will be scenarios of  "works on my machine" which is really tough to deal with.

Debugging IIS hosted Web Forms Unit test

By default if we put a break point in ASP.Net Web Forms unit test where the site is hosted in IIS, it wont get hit. We need to attach to IIS process w3wp.exe.

So put a Debugger.Break or Debugger.Launch. This will open up an error dialog and from there we can attach to W3WP.exe.1

Also need to make sure that the dlls are compiled in debug mode.

Issues

Sometime there may be issues when running the unit tests. One error what I faced was below.

The URL specified ('http://localhost/joymononline') does not correspond to a valid directory. Tests configured to run in ASP.NET in IIS require a valid directory to exist for the URL. The URL may be invalid or may not point to a valid Web application

There are multiple reasons for this.

First we need to run Visual Studio in admin privilege. 

Second we need check whether the below entry is logged into event log viewer -> application  node

(QTAgent32_40.exe, PID 12920, Thread 14) WebSites.GetWebServer: failed to create AspNetHelper: Microsoft.VisualStudio.Enterprise.Common.AspNetHelperException: The website metabase contains unexpected information or you do not have permission to access the metabase.  You must be a member of the Administrators group on the local computer to access the IIS metabase. Therefore, you cannot create or open a local IIS Web site.  If you have Read, Write, and Modify Permissions for the folder where the files are located, you can create a file system web site that points to the folder in order to proceed. ---> System.Runtime.InteropServices.COMException: Unknown error (0x80005000)
   at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
   at System.DirectoryServices.DirectoryEntry.Bind()
   at System.DirectoryServices.DirectoryEntry.get_IsContainer()
   at System.DirectoryServices.DirectoryEntries.ChildEnumerator..ctor(DirectoryEntry container)
   at System.DirectoryServices.DirectoryEntries.GetEnumerator()
   at Microsoft.VisualStudio.Enterprise.Common.IISHelper.GetWebServerOrdinal(Uri site)
   --- End of inner exception stack trace ---
   at Microsoft.VisualStudio.Enterprise.Common.IISHelper.GetWebServerOrdinal(Uri site)
   at Microsoft.VisualStudio.Enterprise.Common.IISHelper.get_WebServerOrdinal()
   at Microsoft.VisualStudio.Enterprise.Common.IISHelper.get_RootPath()
   at Microsoft.VisualStudio.Enterprise.Common.IISHelper.get_PhysicalPath()
   at Microsoft.VisualStudio.Enterprise.Common.AspNetHelperMan..ctor(Uri uri, BasicAuthCredential credential, Int32 frameworkMajorVersion)
   at Microsoft.VisualStudio.Enterprise.Common.AspNetHelperMan..ctor(Uri uri, BasicAuthCredential credential)
   at Microsoft.VisualStudio.Enterprise.Common.AspNetHelper.Create(Uri uri, BasicAuthCredential credential)
   at Microsoft.VisualStudio.TestTools.HostAdapters.WebSites.GetWebServer(String webServerName, WebServerType webServerType, String urlToTest, String pathToWeb, String webAppRoot, BasicAuthCredential credential, Context context, WebSiteConfigurationType webSiteConfigType, Origin origin)

If we can see the above, we can try enabling IIS 6 compatibility. This is applicable if we are running on Windows 7 with IIS 7 or above.

Go to Control Panel->Programs and Features->Turn Windows Featuers On or Off
Then
Internet Information Services->Web Management Tools->IIS 6 Management Compatibility Tick the "IIS Metabase and IIS 6 configuration compatibility" option and install it.

Some users says that we need to enable everything in IIS 6 Management Compatibility.2

References

1http://forums.asp.net/t/1246417.aspx?How+to+Debug+unit+tests+for+ASP+NET+web+application
2http://stackoverflow.com/questions/9065937/asp-net-unit-testing-windows7-iis7

Tuesday, May 5, 2015

Extensible Chaos Monkey

Chaos Monkey

Chaos Monkey is a great concept introduced by Netflix to create random issues in cloud server environments so that those issues can be addressed early and test against unexpected failures. That makes sure any system can recover from any kind of failure which can happen in production.

The good news is that they didn't keep it secret. They made that tool online including source in Github. It is written in Java so it was a little difficult for the .Net community to extend. There are many .Net ports of the same tool or tried to implement the same.

The problem

In our company also things are not different from any other production environment. There are issues which are first reported / only happens in production. We have an online audit system that contains many backend processing components. The backend queueing system is developed in such a way that any application track developer can add a new queue type and write it's associated handler. Though there are guidelines for the backend developers to roll back properly, there are times when developers are not caring it to meet the deadlines and it goes to production. In production, we end up with the wrong data states. Some of the reasons are unexpected IIS / AppPool recycling. Database timeout/outage etc...Since the application is not expecting those states, it will be clueless about how to recover from there. Finally, support has to manually run SQL statements in production servers to correct the state.

Ideally, QA is supposed to test scenarios such as IIS reset and all when the backend services are running. But they have limitations. It is very difficult to make sure that they cover all the scenarios.  Ensuring the randomness and tracing the abnormal event to reproduce in dev will be difficult. There are also difficulties in manually creating abnormal scenarios when the tests are running overnight.

Solution

All these things lead to a testing strategy where issues / abnormal scenarios that are expected in production needs to created in dev/QA environments so that we can identify how the system behaves to those abnormal events and sometimes change the application flow to recover those states.

This is the point we started looking towards already existing ChaosMonkey solution. It seems suitable for us. But we are not able to use it straight away because ChaosMonkey is targeted towards cloud but our production deployment is in house.

Why extensible

So we tried to extend the tool. But since its in Java and most of our developers are familiar with .Net ecosystem, we started looking for .Net port of Chaos Monkey. Unfortunately not able to find much.  Though there are some they are also targeted towards the cloud. To restart the local IIS server, had to write a lot of code. 

So finally decided to take one as a base and make it extensible to meet the scenario. Forked ChaosMonkey implementation from Githb by Simonmnro and added my own changes to make it pluggable.

My version of Extensible chaos monkey is available in the below location.

What's Next

  • Clean separation of plugin code and config
  • Adding more plugins such as increase memory pressure.
Feel free to comment if there is a better solution to the issue and contact me if willing to contribute to the project.

Tuesday, February 24, 2015

Group by test class name in VS2012 Test Explorer

Background

Writing unit test case is always a good practice in programming. It gives confidence to re-factor code and make sure each and every piece is working properly. If individual pieces are working good mostly the application will work after integration.

Basics of Visual studio unit test framework

Visual Studio has out of the box unit testing framework. We can create test classes and those classes can contain test methods. Normally if we follow proper naming convention to relate production code with test projects, it is easy to see how the tests are performing. One of the naming convention for unit test class and methods names can be as follows
  • Let the test project name be same as production project but suffix the test project with .Tests
  • For each production code method, create a unit test class.
  • Each scenario in the production method can be one test method in test class.
  • Test class name can be <Scenario>_<Production Method Name>{}
  • Method name can be <Test case specification>_<Expected behavior>()
eg: Production code

    public static class ObjectFactory
    {
        public static object CreateObject(string fullyQualifiedClassAndAssemblyName, object[] constructorParameters)
        {
//Production code which uses reflection to create object.
        }
    }

Unit test class

[TestClass, ExcludeFromCodeCoverage]
public class ObjectFactory_CreateObject
{
        [TestMethod, Timeout(1000)]
        public void WhenFullyQualifiedClassAndAssemblyNamePassedAndClassIsInternal_ShouldSucceed()
        {
//Test code
        }
        [TestMethod,ExpectedException(typeof(FileNotFoundException))]
        public void WhenFullyQualifiedClassAndNonExistingAssemblyNamePassed_ThrowFileNotFoundException()
        {
//Test code
        }
}

The main benefit of this naming convention is, it does not need any additional documentation to describe the what the test does. When we avoid one more item from maintenance, we are increasing productivity.

Running test cases from visual studio

We can easily trigger the unit test execution and see the results in "Test Results" window in VS 2010. The results will be shown with test name by default. The same functionality is there in VS2012 as well, but the window name is changed from "Test Results" to "Test explorer".

Problem

When the project grows, there are high chance that programmers forget to write test cases. Or even if there is strict enforcement, they will tend to write one test method like below for each and every production method.

[TestClass]
public class EmailSender_Send
{
        [TestMethod]
        public void WithProperparameter_ShouldSucceed()
        {
//Test code
        }
}
[TestClass]
public class Scheduler_CreateSchedule
{
        [TestMethod(), Timeout(30000)]
        public void WithProperParameters_ShouldSuccessed()
        {
//Test code
        }
}

Normally the test results will be displayed only with the test name and the test name is the test method name. That ends up in the test result window with all tests with same name. Its very difficult to understand from the review perspective.

If we want to see the test class name we can group by the test class name as below. This works well in VS2010.
[Photo taken from http://www.ladislavmrnka.com/wp-content/uploads/2012/09/TestView.png]

The same functionality in VS2012 named Test Explorer doesn't have Group by Class name in its earlier production releases. The group by popup is shown as below when try to group by class name.
This is really useless for somebody who want to see the test results. There is no idea why Microsoft removed a useful feature from earlier version in new release. But fortunately its back in later VS2012 updates. When this post is written, the latest update was Update 4.

Happy coding. Yes unit testing is part of coding:)

Tuesday, December 9, 2014

Unit testing - How to verify dependent object's methods are called?

Testing void methods

In one of previous post, we saw what is mocking in unit testing. If the function which we are going to unit test is returning a value also expecting some value from the dependency which we mocked, its easy to test. But how to test a method which doesn't return a value, but successful execution calls a method in the dependency. In other sense we need to know whether a particular method in mock gets called or not to determine the test result.

So this post is aiming at how to test void methods which calls a particular method in dependency. Hope the purpose on why we should check whether a method in dependency is called or not is clear.

If its not clear, lets take one scenario. Sorry my project mates felt it difficult to understand so I am thinking there would be similar kind of developers out there. Others can skip the below story and look at the code.

Take the scenario of saving a Person object into database. There is a PersonRepository class with Save() method. It needs to validate some business rules such as the phone number and email. When we test the void PersonRepository.Save() by writing unit test cases, we must be focused only on the logic inside the Save(), not about saving into database. That will be done by IDataAccess which is a dependency of PersonRepository. We need to make sure that the Save() method is calling IDataAccess.InsertOrUpdate() upon success. 

Below is the code snippet which is ensuring that a method of dependency is called from production code upon success. It uses Moq library.

    public class PersonRepository
    {
        IDataAccess _dataAccess;
        public PersonRepository(IDataAccess dataAccess)
        {
            _dataAccess = dataAccess;
        }
        public void Save(Person person)
        {
            ThrowExceptionIfInvalid(person);
            _dataAccess.InsertOrUpdate(person);
        }
        private void ThrowExceptionIfInvalid(Person person)
        {
            if(string.IsNullOrEmpty(person.EMail))
            {
            throw new ArgumentNullException("e-Mail should not be empty");
            }
            //More code goes here to validate email & phone
        }
    }

Unit test code

        [TestMethod()]
        public void WhenEverythingIsOK_CallIDataAccessSave()
        {
            Mock<IDataAccess> mockDataAccess = new Mock<IDataAccess>();
 
            Person person = new Person() { EMail = "joymon@gmail.com" };
            PersonRepository repository = new PersonRepository(mockDataAccess.Object);
            repository.Save(person);//Test only the logic written inside Save() method.

            //IDataAccess.InserOrUpdate() will be called on success
            mockDataAccess.Verify(mock => mock.InsertOrUpdate(person), Times.Exactly(1));
        }

Happy testing.