[go: up one dir, main page]

0% found this document useful (0 votes)
16 views38 pages

Mocking in Csharp

The document discusses best practices for unit testing in .NET applications, including the use of multiple target frameworks, FluentAssertions for assertions, and the Moq framework for mocking dependencies. It emphasizes the importance of Dependency Injection (DI) for creating flexible and testable code, detailing how to implement DI using IoC containers. Additionally, it provides examples of factory patterns and the significance of managing service lifetimes and dependencies effectively.

Uploaded by

Hans Jones
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views38 pages

Mocking in Csharp

The document discusses best practices for unit testing in .NET applications, including the use of multiple target frameworks, FluentAssertions for assertions, and the Moq framework for mocking dependencies. It emphasizes the importance of Dependency Injection (DI) for creating flexible and testable code, detailing how to implement DI using IoC containers. Additionally, it provides examples of factory patterns and the significance of managing service lifetimes and dependencies effectively.

Uploaded by

Hans Jones
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

Unit Testing, Mocking, Dependency Injection,

Code Complexity and Publish .NET apps


Testing against multiple frameworks 1
• When we create a .NET standard library, we can target multiple frameworks/platforms at once
• We should however test that the code runs well on all these platforms. Ex: if we create a .NET
Standard 2.1 library we need to test it on >= .NET 2.x. .NET Framework does not support 2.1
https://docs.microsoft.com/en-us/dotnet/standard/net-standard

2) The versions listed


here represent the
7.0, 8.0, 9.0 rules that NuGet uses
to determine whether
a given .NET
Standard library is
applicable.

3) .NET Framework
won't support .NET
Standard 2.1 or later
versions. For more
details, see the
announcement
of .NET 5+ TFMs.

Target frameworks in SDK projects: https://docs.microsoft.com/en-us/dotnet/standard/frameworks


Testing against multiple frameworks 2
• Using our test .csproj file, we can set multiple target frameworks - and the test
runner/adapter will use all of them when it performs the tests
• Open the .csproj file (as described in earlier presentations) of the test project
• Replace TargetFramework by TargetFrameworks (plural) and set the desired frameworks
separated by a semicolon
• The list of target frameworks is available in the documentation:
https://docs.microsoft.com/en-us/dotnet/standard/frameworks
• As of this writing, latest valid <PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0;net5.0;netcoreapp3.1;netstandard2.1;net481
combo for all frameworks are → </TargetFrameworks>
<IsTestProject>true</IsTestProject>
• In VS you will get 7 runs in <...>...</...>
</PropertyGroup>
the output view
• It may be better to use the console:
”dotnet test” which will show more info
• Download the latest SDK versions here:

https://dotnet.microsoft.com/download
C# FluentAssertions in .NET apps
• FluentAssertions is one of the best assertion frameworks in the .NET realm because of it syntax
• Fluent Assertions supports all unit test using FluentAssertions;
frameworks [Fact]
public void HelloManShouldBeWellFormated()
• Install FluentAssertions via NuGet {
https://fluentassertions.com/ var hello = new Hello("John", "Doe"); // Arrange
var helloMan = hello.HelloMan(); // Act
public class Hello // Assert
{ helloMan
private string _firstName { get; set; } .Should().StartWith("Hello")
private string _lastName { get; set; } .And.EndWith("!") HelloFluentAssertions
.And.Contain("John") HelloFluentAssertionsTest
public Hello(string firstName, string lastName) .And.Contain("Doe");
{
}
_firstName = firstName;
[Fact]
_lastName = lastName;
} public void HelloManShouldBeRaiseExceptionWhenFirstNameIsNotSet()
{
public string HelloMan() var hello = new Hello("", "Doe"); // Arrange
{ Action actionHelloMan = () => hello.HelloMan(); // Act
if (string.IsNullOrEmpty(_firstName)) // Assert
throw new MissingFirstNameException(); actionHelloMan
.Should()
return $"Hello {_firstName} {_lastName} !"; .Throw<MissingFirstNameException>()
}
.WithMessage("FirstName is missing");
...
}
C# Mocking
• Moq Framework - The most popular and friendly mocking framework for .NET

https://github.com/moq/ - Moq 4.x is the current version (5.x is on the way)
• Moq is usable with any test runner like MSTest, xUnit, NUnit, etc.
• When using Moq or any other mocking framework, keep the following
restrictions in mind which you cannot mock

Static class/methods

Extensions Methods

The only difference between a regular static method and an extension method is that the first
parameter of the extension method specifies the type that it is going to operate on, preceded by
the this keyword as: MethodName(this type1 p1, type2 p2) { ... }

Call with: type1.Methodname(p2) AutofacWindsorDelegate

Example: https://www.tutorialsteacher.com/csharp/csharp-extension-method

Non-virtual methods (except if there is an interface)

In fact interfaces or virtual methods is actually what you mostly use to mock
C# Mocking with Moq 1
• To generate a Moq mock you have to create a new Mock object and set the type (Class or Interface)
Some constructor overloads exist
• Setup() specifies a setup on the mocked type for a call to a value returning method
• Returns() specifies the value to return
• To execute the mocked instance call: myMock.object.MockedMethod()
• Various Mock examples from: https://github.com/devlooped/moq/wiki/Quickstart
/* To generate a mock you just have to create a new Mock object and set the type. public interface IFoo
Initializes an instance of the mock with Moq.MockBehavior.Default and with the {
given constructor arguments for the class. (Only valid when T is a class)
Bar Bar { get; set; }
public Mock<T>();
public Mock<T>(params object[] args); string Name { get; set; }
public Mock<T>(MockBehavior behavior); int Value { get; set; }
public Mock<T>(MockBehavior behavior, params object[] args); */ bool DoSomething(string value);
var myMock = new Mock<IFoo>(); bool DoSomething(int number, string value);
string DoSomethingStringy(string value);
// Specifies a setup on the mocked type for a call to a value returning method bool TryParse(string value,
myMock.Setup(foo => foo.DoSomething("ping")).Returns(true); out string outputValue);
bool Submit(ref Bar bar);
// mock.Object.Method(... Exposes the mocked object instance long GetCount();
Console.WriteLine($"DoSomething blabla returns: bool Add(int value);
{myMock.Object.DoSomething("blabla")}"); // false
} More examples
Console.WriteLine($"DoSomething ping returns:
{myMock.Object.DoSomething("ping")}"); // true
in MoqDemo
C# Mocking with Moq 2
• Moq.It.Is*(), overloads and Throws() etc.
var myMock = new Mock<IFoo>();
// access invocation arguments when returning a value - Moq.It.*** (specify a matching condition)
myMock.Setup(x => x.DoSomethingStringy(It.IsAny<string>())).Returns((string s) => s.ToLower());
Console.WriteLine($"DoSomethingStringy BB returns: {myMock.Object.DoSomethingStringy("BB")}");

// Multiple parameters overloads available


// throwing when invoked with specific parameters
myMock.Setup(foo => foo.DoSomething("reset")).Throws<InvalidOperationException>();
myMock.Setup(foo => foo.DoSomething("")).Throws(new ArgumentException("command"));
try {
Console.WriteLine($"DoSomething throws: {myMock.Object.DoSomething("reset")}");
Console.WriteLine($"DoSomething throws: {myMock.Object.DoSomething("")}");
}
catch (InvalidOperationException e) {
Console.WriteLine($"DoSomething reset: {e.ToString()}");
}
catch (ArgumentException e) {
Console.WriteLine($"DoSomething empty string: {e.ToString()}");
}

// lazy evaluating return value via Lambda expression


myMock.Setup(foo => foo.GetCount()).Returns(() => DateTime.Now.Ticks); More examples
Console.WriteLine($"GetCount returns: {myMock.Object.GetCount()}");
Console.WriteLine($"GetCount returns: {myMock.Object.GetCount()}"); in MoqDemo
C# Mocking with Moq 3
• Moq.It.Is*(), Moq.It.Ref(), matching args, properties, events, callbacks, verification, etc.
var myMock = new Mock<IFoo>();
// Matching Arguments of any string value - - Moq.It.*** (specify a matching condition) More examples
myMock.Setup(foo => foo.DoSomething(It.IsAny<string>())).Returns(true);
Console.WriteLine($"DoSomethingS AnyString returns: {myMock.Object.DoSomething("AnyString")}");
in MoqDemo
// matching ranges (4 is in 0-10)
myMock.Setup(foo => foo.Add(It.IsInRange<int>(0, 10, Range.Inclusive))).Returns(true);
Console.WriteLine($"Add IsInRange<int> 4 returns: {myMock.Object.Add(4)}");
// Properties
myMock.Setup(foo => foo.Name).Returns("bar");
Console.WriteLine($"Name returns: {myMock.Object.Name}");
// Callbacks - Specifies a callback to invoke when the method is called
var calls = 0;
myMock.Setup(foo => foo.DoSomething("ping")).Callback(() => calls++).Returns(true);
Console.WriteLine($"DoSomething(ping) Callback returns: {myMock.Object.DoSomething("ping")}");
Console.WriteLine($"GetCount() Callback returns: {myMock.Object.GetCount()}");
// Verification - Verifies that a specific invocation matching the given expression was performed on the mock.
// When you are using the verification from an mock instance by calling moq.Verify() it does check if all set up methods are called
myMock.Setup(foo => foo.DoSomething("ack")).Returns(true).Verifiable("ack test");
Console.WriteLine($"DoSomething(ack) returns: {myMock.Object.DoSomething("ack")}");
// Verifies that all verifiable expectations have been met. If not an exception is generated.
myMock.Verify(foo => foo.DoSomething("ack"));
// Events – much more code is needed
var myMock2 = new Mock<IFoo2>();
myMock2.SetupAllProperties();
myMock2.SetupProperty(m => m.FooEvent);
myMock2.Raise(m => m.FooEvent += null, new FooEventArgs(fooValue));
IoC, DIP, DI and IoC containers
• IoC – Loose coupling between classes
• DIP – High-level modules should not depend
on low level modules. Both both should
depend on abstractions. Abstractions should
not depend on details. Details should depend
upon abstractions.
• DI – A design pattern which implements the
IoC principle to invert the creation of
dependent objects
• IoC container – A framework used to manage
automatic dependency injection
https://www.tutorialsteacher.com/ioc/
public static class VehicleFactory
{
public static Ivehicle Build(int numberOfWheels)
Factory Pattern
{ public interface Ivehicle {}
switch (numberOfWheels)
{ public class Unicycle : Ivehicle {}
case 1: public class Car : Ivehicle {}
return new Unicycle(); public class Motorbike : Ivehicle {}
case 2: public class Truck : Ivehicle {}
case 3:
return new Motorbike();
case 4:
return new Car();
default :
return new Truck();
}
}
}

public static class FactoryPattern


{
public static void Create()
{
Console.WriteLine("Enter a number of wheels between 1 and 12 to build a vehicle and press enter");
var wheels = Console.ReadLine();
var vehicle = VehicleFactory.Build(Convert.ToInt32(wheels));
Console.WriteLine($"You built a {vehicle.GetType().Name}");
Console.Read();
}
} AutofacWindsorDelegate: IocFactoryPattern.cs
Dependency Injection (DI) 1
• What is Dependency Injection?

It's a programming technique that makes a class independent of its dependencies

We don't rely on a concrete implementation of our dependencies, but rather interfaces
• Why use it? There are many advantages, as

Flexible code, we can switch out one implementation for another one without changing the
business logic

Easy to test, because we rely on interfaces over implementations - we can more easily test our
code without worrying about side-effects
• The Ultimate List of .NET Dependency Injection Frameworks

https://www.claudiobernasconi.ch/2019/01/24/the-ultimate-list-of-net-dependency-injection-
frameworks/
• In .NET there's a built-in DI container that's used by a lot of internal services like

Hosting Environment, Configuration, Routing, MVC, Application Lifetime, Logging, etc.

Sample: https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
Dependency Injection (DI) 2
• The container is sometimes referred to as IoC (Inversion of Control) container
and it lives in the Microsoft.Extensions.DependencyInjection namespace

The overall idea is to Register Services (interfaces and their concrete implementations) at the
application startup and then Resolve them at runtime when needed
• Other Nuget IoC packages Moq.AutoMocker IoC container

Moq.AutoMocker https://github.com/moq/Moq.AutoMocker

Autofac Castle Windsor IoC container

Windsor Castle Project http://www.castleproject.org/
• Container responsibilities Autofac IoC container
https://autofac.org

Creating (decouple changes in constructor args)

Disposing AutofacWindsorDelegate example

IServiceCollection : Register services, lets the IoC container know of concrete implementation. It
should be used to resolve what Interface belongs to what implementation.
How something is created can be as simple as just instantiating an object but sometimes we need
more data than that

IServiceProvider : Resolve service instances, actually looking up what interface belongs to what
concrete implementation and carry out the creation
Dependency Injection (DI) 3
• What services to register?
• There are some telltale signs

Lifespan outside of this method?, Are we new-ing the service, any services that
can they live within the scope of the method? I.e. are they a dependency or not?

More than one version, Can there be more than one version of this service?

Testability, ideally you only want to test a specific method. If you got code that does
a lot of other things in your method, you probably want to move that to a dedicated
service. This moved code would then become dependencies to the method in
question

Side-effect, This is similar to the point above but it stresses the importance of having
a method that does only one thing. If a side-effect is produced, i.e. accessing a
network resource (an HTTP call) or interacting with I/O (storage access), then it
should be placed in a separate service and injected as a dependency
• Essentially, you will end up moving out code to dedicated services and then
inject these services as dependencies via a constructor
Dependency Injection (DI) 4
• You might start out with code looking like below which has many problems

Unwanted side-effects when testing, The first problem is that we control the lifetime of
PaymentService and ShippingService, thus risking firing off a side-effect, as an HTTP call when trying
to test

Can't test all paths, we can't really test all paths, we can't ask the PaymentService to respond
differently so we can test all execution paths

Hard to extend, will this PaymentService cover all the possible means of payment or would we need
to add a lot of conditional code in this method to cover different ways of taking payment if we added
say support for PayPal or a new type of card, etc?

Unvalidated Primitives, there are primitives like double and string. Can we trust those values - is the
address a valid address for example?
public void ActionNoDi(double amount, string cardNumber, string address, string city, string name)
{
var paymentService = new PaymentService();
var successfullyCharged = paymentService.Charge(int amount, cardNumber);

if (successfullyCharged)
{
var shippingService = new ShippingService(); DepInjDemo
shippingService.Ship(address, city, name);
}
}
Dependency Injection (DI) 5
• From previous, we realize that we need to refactor our code into something more maintainable and
more secure. Turning a lot of the code into dependencies and replacing primitives with more
complex constructs is a good way to go
• The result could look something like below. We have turned both the PaymentService and
ShippingService into dependencies that we inject into the constructor
• We also see that all the primitives have been collected into the complex structures
IShippingAddress and IPaymentInfo. What remains is pure business logic
class TransactionController
{
private readonly IPaymentService _paymentService;
private readonly IShippingService _shippingService;

public void TransactionController(IPaymentService paymentService, IShippingService shippingService) {


_paymentService = paymentService;
_shippingService = shippingService;
}

public void Action(IPaymentInfo paymentInfo, IShippingAddress shippingAddress) {


var successfullyCharged = _paymentService.Charge(paymentInfo);
DepInjDemo
if (successfullyCharged) {
_shippingService.Ship(ShippingAddress);
} Similar demo as the
}
} API DepInjWebApi
DepInjWebApi
Dependency Injection (DI) 6
• Class diagrams for DepInjDemo and
DepInjWebApi in DependencyInjection.zip

More examples
in the DI
project
Dependency Injection (DI) 7
• Dependency Graph

When you have a dependency it might itself rely on another dependency being resolved first and
so on and so forth. This means we get a hierarchy of dependencies that need to be resolved in
the right order for things to work out. We call this a Dependency Graph
• References

The Personnummer sample exam use both Autofac and Castle Windsor IoC containers

IamTimCorey (Design Patterns and Dependency Injection in C#)

https://www.youtube.com/watch?v=NnZZMkwI6KI

https://www.youtube.com/watch?v=mCUNrRtVVWY

Inversion of Control Containers and Dependency Injection pattern by Martin Fowler

https://www.martinfowler.com/articles/injection.html

Overview of Dependency Injection, DI MVC examples, DI in Controllers etc.

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/dependency-injection

https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection

Overview of Dependency Injection namespace

https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection
Mocking a WebRequest
• The IWebRequest interface has in this case a Create() method that all classes have to implement

We can mock this method by using the Moq Setup() method
• When you want to mock a method with parameters you can work with the Moq.It.IsAny<type>()
option – used when argument value for a method call is not relevant
• In this example all string values are allowed
[Fact]
public void ShouldReturnContentStringWhenStatusCodeIsOk() LinkReader
{ LinkReaderTest
var resultContent = "<html><b>I am the test</b></html>";
var resultContentBytes = Encoding.ASCII.GetBytes(resultContent); (HtmlRetrieverTest.cs)
var moq = new Mock<IWebRequest>();
var moqHttpWebRequest = new Mock<HttpWebRequest>();
var moqHttpWebResponse = new Mock<HttpWebResponse>();
// Discards (from C# 7.0): https://docs.microsoft.com/en-us/dotnet/csharp/discards
moqHttpWebResponse.Setup(_ => _.StatusCode).Returns(HttpStatusCode.OK); // same as: x => x.StatusCode
moqHttpWebResponse.Setup(_ => _.GetResponseStream()).Returns(new MemoryStream(resultContentBytes));
moqHttpWebRequest.Setup(_ => _.GetResponse()).Returns(moqHttpWebResponse.Object);

moq.Setup(_ => _.Create(It.IsAny<string>())).Returns(moqHttpWebRequest.Object);

var retriever = new HtmlRetriever(moq.Object); // Exposes the mocked object instance


var result = retriever.Retrieve("test"); // URL does not matter since Requests are mocked
// xUnit with FluentAssertions
result.Should().Be(resultContent); // Otherwise it is: Assert.Equal(result, resultContent);
}
Tim Corey
Mocking a DBRequest https://www.youtube.com/watch?v=DwbYxP-etMY
MockingDemo Start and End

• Moq provides different ways to set up the mock -


https://github.com/Moq/moq4/wiki/Quickstart#customizing-mock-behavior
• Here we mock a DB call so [Fact]
public void LoadPeople_ValidCall()
we do not have to care about {
// Create new Autofac.Extras.Moq.AutoMock instance with loose mock behavior
the DB using (var mock = AutoMock.GetLoose()) // loose is the default
{
mock.Mock<ISqliteDataAccess>()
• Different ways to set up the //.Setup(x => x.LoadData<PersonModel>("select * from Person"))
.Setup(_ => _.LoadData<PersonModel>("select * from Person"))
mock .Returns(GetSamplePeople());

Mock<IFoo>(MockBehavior.Strict); // Create mocked PersonProcessor class
var cls = mock.Create<PersonProcessor>();

Mock<IFoo>(MockBehavior.Loose); var expected = GetSamplePeople(); // get a hard coded list of people


Mock<IFoo> { CallBase = true }; var actual = cls.LoadPeople(); // mocked DB call


Mock<IFoo> Assert.True(actual != null);
Assert.Equal(expected.Count, actual.Count);
{ DefaultValue = DefaultValue.Mock }
for (int i = 0; i < expected.Count; i++)

MockRepository(MockBehavior.Strict) {
{ DefaultValue = DefaultValue.Mock }; Assert.Equal(expected[i].FirstName, actual[i].FirstName);
Assert.Equal(expected[i].LastName, actual[i].LastName);
}
}
} // gmi2j3/docs.and.code/YouTube/Mocking in C# Unit Tests
C# Mocking with Moq Channel9 - 1
UnitTesting.MOQExamples
• We can also send the created mock object to another class via DI (A_FirstExample.cs)
• No actual code exist in the implementation FakeRepo namespace MOQExamples.SystemsUnderTest;
• If the faked methods gets called they execute public interface IRepo
throw new NotImplementedException(); {
Customer Find(int id);
[Fact] void AddRecord(Customer customer);
public void Should_Mock_Function_With_Return_Value() }
{ public class Customer
// Arrange {
var id = 12; public virtual int Id { get; set; }
var name = "Fred Flinstone"; public virtual string Name { get; set; }
var customer = new Customer {Id = id, Name = name}; }
var mock = new Mock<IRepo>(); public class TestController
{
mock.Setup(x => x.Find(id)).Returns(customer);
public TestController(IRepo repo, ILogger logger = null)
{
// the injector (this class) register a service (customer) to a controller _repo = repo;
// and the interface define how a client may use the service _logger = logger;
// https://en.wikipedia.org/wiki/Dependency_injection }
var controller = new TestController(mock.Object); public Customer GetCustomer(int id)
{
// Act try {
var actual = controller.GetCustomer(id); _repo.AddRecord(new Customer());
// Assert return _repo.Find(id);
Assert.Same(customer, actual); } catch (Exception ex) { throw; }
Assert.Equal(id, actual.Id); }
Assert.Equal(name, actual.Name); }
} // docs.and.code/channel9.msdn.com
C# Mocking with Moq 4
• When you are using the verification from an mock instance by calling moq.Verify() it does
check if all set up methods are called
• mock.Verify() Verifies that all verifiable expectations have been met
• mock.VerifyAll() Verifies all expectations regardless of whether they have been flagged as
verifiable
[Test] Twitter
public void ReceiveMessageShouldInvokeItsClientToWriteTheMessage()
Twitter.Tests
{
// Arrange
var client = new Mock<IClient>();
client.Setup(c => c.WriteTweet(It.IsAny<string>()));
var tweet = new Tweet(client.Object);

// Act - if Moq ReceiveMessage is not called test fails


tweet.ReceiveMessage("Test");

// Assert
// (Mock.Verify) Verifies that the method is Invoked during the Test exactly 1 time
client.Verify(c => c.WriteTweet(It.IsAny<string>()), Times.Once,
"Tweet doesn't invoke its client to write the message");
}
C# Mocking with Moq Channel9 - 2
• Verifiable() Marks the expectation as verifiable, meaning that a call to
Moq.Mock.Verify() will check if this particular expectation was met, and
specifies a message for failures
[Fact] UnitTesting.MOQExamples
public void Should_Verify_Times_Executed()
{ (B_Verification.cs)
// Arrange
var id = 12;
var name = "Fred Flinstone";
var customer = new Customer { Id = id, Name = name };
var mock = new Mock<IRepo>();
Expression<Func<IRepo, Customer>> call = x => x.Find(id);
mock.Setup(call).Returns(customer).Verifiable("Method not called");
// Act
var controller = new TestController(mock.Object);
var actual1 = controller.GetCustomer(id);
//var actual2 = controller.GetCustomer(id);
// Assert
mock.Verify(call, Times.Once);
}
C# Mocking with Moq 5
• Multiple Mock objects and dependency injection
public interface IWriter
{ Twitter
private const string Message = "Test"; void WriteLine(string message); Twitter.Tests
}
[Test] public interface ITweetRepository
public void SendTweetToServerShouldSendTheMessageToItsServer() {
{ void SaveTweet(string content);
}
// Arrange
public class MicrowaveOven : IClient
var writer = new Mock<IWriter>(); {
var tweetRepo = new Mock<ITweetRepository>(); private IWriter writer;
tweetRepo.Setup(tr => tr.SaveTweet(It.IsAny<string>())); ITweetRepository tweetRepo;
var microwaveOven =
new MicrowaveOven(writer.Object, tweetRepo.Object); public MicrowaveOven(IWriter writer,
ITweetRepository tweetRepo)
{
// Act this.writer = writer;
microwaveOven.SendTweetToServer(Message); this.tweetRepo = tweetRepo;
}
// Assert
tweetRepo.Verify(tr => public void SendTweetToServer(string message) =>
tr.SaveTweet(It.Is<string>(s => s == Message)), this.tweetRepo.SaveTweet(message);
Times.Once,
public void WriteTweet(string message) =>
"Message is not sent to the server"); this.writer.WriteLine(message);
} }
[TestMethod]
public void TestDatabaseMocking()

C# {
int size = 5;
string sql = "select * from EmployeeTable";
Polymorphism and
PolymorphismTest

Mocking // Arrange
List<Employee> expectedEmployees = new List<Employee>();
for (int i = 0; i < size; i++)
{

with }
expectedEmployees.Add(new Employee() { Name = "Kalle", Hours = Utils.HOURS });
expectedEmployees.Add(new Contractor() { Name = "Pelle", Company = "HDA" });

Moq 6 // Act using an interface


var mock = new Mock<IDataBase>();
mock.Setup(_ => _.LoadData<Employee>(sql)).Returns(AssignData(size));
List<Employee> employees = mock.Object.LoadData<Employee>(sql);

// Assert
• Polymorphism for (int i = 0; i < expectedEmployees.Count; i++)
{
is an example Assert.AreEqual(expectedEmployees[i].Name, employees[i].Name);
Assert.AreEqual(expectedEmployees[i].Hours, employees[i].Hours);
dealing with }

mocking // Act - other way using virtual methods in a class, but the class could as well implement an interface
var mockUtil = new Mock<Utils>();
simple web mockUtil.Setup(_ => _.LoadEmployees(sql)).Returns(AssignData(size));
var barDb = new BarDb(mockUtil.Object);
access, virtual List<Employee> barDbEmployees = barDb.LoadEmployees(sql);

methods and // Assert


for (int i = 0; i < expectedEmployees.Count; i++)
DB access {
Assert.AreEqual(expectedEmployees[i].Name, barDbEmployees[i].Name);
etc. }
Assert.AreEqual(expectedEmployees[i].Hours, barDbEmployees[i].Hours);

}
public interface IDataBase
{

C# List<T> LoadData<T>(string sql);


void SaveData<T>(T person, string sql);
void UpdateData<T>(T person, string sql);
Polymorphism and
PolymorphismTest
}

Mocking // A class with virtual methods we mock, mocks must be virtual or interface
public class Utils

with
{
public const int HOURS = 40;
public virtual List<Employee> GetMockEmployees() { throw new NotImplementedException(); }
public virtual bool WriteEmployees(List<Employee> employees) { throw new NotImplementedException(); }

Moq 7 }
public virtual List<Employee> ReadEmployees() { throw new NotImplementedException(); }
public virtual List<Employee> LoadEmployees(string sql) { throw new NotImplementedException(); }

public class BarDb


{
• Polymorphism private readonly Utils util;
public BarDb(Utils util)
project is also {
a fully working }
this.util = util;

example doing public List<Employee> LoadEmployees(string sql)


{
the mocked return util.LoadEmployees(sql);
}
stuff (except }

DB access) [Serializable]
public class Employee
{
public string Name;
public int Hours { get; set; }
// continues ...
NSubstitute 1
 A friendly substitute for .NET mocking frameworks as Moq made by the NUnit guys
 Install NuGet packages: NSubstitute and NSubstitute.Analyzers.CSharp
 Good documentation and maybe a bit easier than Moq
[Test]
public void TestMethod1ReturnsEqual() https://nsubstitute.github.io/
{
// basic syntax for creating a substitute is
var calculator = Substitute.For<ICalculator>(); namespace NsubstituteDemo;
// tell our substitute to return a value for a call
calculator.Add(1, 2).Returns(3); public interface ICalculator
Assert.That(calculator.Add(1, 2), Is.EqualTo(3));
{
// check that our substitute received a call, and did not receive others // method
calculator.Add(1, 2);
calculator.Received().Add(1, 2); int Add(int a, int b);
calculator.DidNotReceive().Add(5, 7); // property
// If our Received() assertion fails, NSubstitute tries to give us some help
// as to what the problem might be string Mode { get; set; }
// event
// We can also work with properties using the Returns syntax we use for methods
calculator.Mode.Returns("DEC"); event EventHandler PoweringUp;
Assert.That(calculator.Mode, Is.EqualTo("DEC")); }
// or just stick with plain old property setters (for read/write properties):
calculator.Mode = "HEX";
Assert.That(calculator.Mode, Is.EqualTo("HEX")); NSubstituteDemo
}
NSubstitute 2
[Test]
public void TestMethod2ReturnsEqual()
{
var calculator = Substitute.For<ICalculator>();
// NSubstitute supports argument matching for setting return values and asserting a call was received:
calculator.Add(10, -5);
calculator.Received().Add(10, Arg.Any<int>());
calculator.Received().Add(10, Arg.Is<int>(x => x < 0));

// We can use argument matching as well as passing a function to Returns() to get some more behaviour out of our substitute
calculator.Add(Arg.Any<int>(), Arg.Any<int>()).Returns(x => (int)x[0] + (int)x[1]);
Assert.That(calculator.Add(5, 10), Is.EqualTo(15));

// Returns() can also be called with multiple arguments to set up a sequence of return values.
calculator.Mode.Returns("HEX", "DEC", "BIN");
Assert.That(calculator.Mode, Is.EqualTo("HEX"));
Assert.That(calculator.Mode, Is.EqualTo("DEC"));
Assert.That(calculator.Mode, Is.EqualTo("BIN"));
}
[Test]
public void TestMethod3EventWasRaised()
{
var calculator = Substitute.For<ICalculator>();
bool eventWasRaised = false; // we can raise events on our substitutes
calculator.PoweringUp += (sender, args) => eventWasRaised = true;

calculator.PoweringUp += Raise.Event();
Assert.That(eventWasRaised);
Console.WriteLine("TestMethod3EventWasRaised: " + eventWasRaised);
NSubstituteDemo
}
C# Mocking Code samples
• Examine the bundled example code
• DependencyInjection
• MockingDemo Start and End with Tim Corey

https://www.youtube.com/watch?v=DwbYxP-etMY
• Presentations-master from channel 9
• cs-code2

MoqDemo

LinkReader and LinkReaderTest

Polymorphism and PolymorphismTest

Twitter and TwitterTest

NSubstituteDemo

HelloFluentAssertions and HelloFluentAssertionsTest

OneDrive > gmi2j3 > lectures > code_csharp

A lot of code is also found in the gmi2j3 > docs.and.code > YouTube folder
Visual Studio Code Metrics
• Analyze > Calculate Code Metrics > ...
• Part of the Roslyn Analyzers packages which analyze your code for style,
quality and maintainability, design and other issues

https://github.com/dotnet/roslyn-analyzers

https://docs.microsoft.com/en-us/visualstudio/code-quality/how-to-generate-code-metrics-data
Code Metrics results
 Maintainability Index - Calculates an index value between 0 and 100 that represents the relative ease of
maintaining the code. A high value means better maintainability. Color coded ratings can be used to quickly identify
trouble spots in your code. A green rating is between 20 and 100 and indicates that the code has good
maintainability. A yellow rating is between 10 and 19 and indicates that the code is moderately maintainable. A red
rating is a rating between 0 and 9 and indicates low maintainability.
 Cyclomatic Complexity - Measures the structural complexity of the code. It is created by calculating the number
of different code paths in the flow of the program. A program that has complex control flow will require more tests to
achieve good code coverage and will be less maintainable.
 Depth of Inheritance - Indicates the number of class definitions that extend to the root of the class hierarchy. The
deeper the hierarchy the more difficult it might be to understand where particular methods and fields are defined
or/and redefined.
 Class Coupling - Measures the coupling to unique classes through parameters, local variables, return types,
method calls, generic or template instantiations, base classes, interface implementations, fields defined on external
types, and attribute decoration. Good software design dictates that types and methods should have high cohesion
and low coupling. High coupling indicates a design that is difficult to reuse and maintain because of its many
interdependencies on other types.
 Lines of Code - Indicates the approximate number of lines in the code. The count is based on the IL (Intermediate
Language) code and is therefore not the exact number of lines in the source code file. A very high count might
indicate that a type or method is trying to do too much work and should be split up. It might also indicate that the
type or method might be hard to maintain.

https://docs.microsoft.com/en-us/visualstudio/code-quality/code-metrics-values
Lizard Code Metrics
• Lizard is an extensible Cyclomatic Complexity Analyzer for many
programming languages which can output statistics in html

cpp, java, csharp, javascript, python, objectivec, ruby, php, swift, scala, ...
• Measures

Cyclomatic Complexity (CCNumber)

LOC (Lines of code)

Token count (number of unique words in the code)

http://www.lizard.ws

Cyclomatic LOC Token Parameter


Function name complexity (1000) count count (100)
(15)
KeesTalksTech.Utilities.Latin.Numerals::Ro 10 44 219 1
manNumeral::ParseRoman
More about the Roslyn Analyzers 1
 Microsoft.CodeAnalysis.NetAnalyzers (.NET 5+)
 Analyze > Run Code Analysis

https://docs.microsoft.com/en-us/visualstudio/code-quality/

Old: Microsoft.CodeAnalysis.FxCopAnalyzers (.NET 3.x)
Starting in .NET 5.0, these analyzers are INCLUDED with the .NET SDK and you don't need to install them separately.
If your project targets .NET 5 or later, some code analysis is enabled by default.

If your project targets a DIFFERENT .NET implementation, for example, .NET Core, .NET Standard, or .NET Framework,
you must manually enable code analysis by setting the EnableNETAnalyzers property to true in your
MSBuild project (.csproj) file by setting the following properties:

1. EnableNETAnalyzers
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>

2. AnalysisLevel
<PropertyGroup>
<AnalysisLevel>latest</AnalysisLevel>
</PropertyGroup>
More about the Roslyn Analyzers 2
 In .NET 5+, code style analysis is configured using a new file type called .editorConfig
1. Go to the project properties and click on Code Analysis and enable all
2. Install the EditorConfig NuGet package
3. Create the .editorConfig file
4. Issues and errors will show up in Error List tab
Source: https://www.c-sharpcorner.com/article/analyzing-code-for-issues-in-net-5/

https://www.youtube.com/watch?v=qv6ZflueASY
SonarQube

Docker
https://docs.sonarqube.org/latest/setup/get-started-2-minutes/
Docker (or Podman)
 Docker is a set of Platform as a Service (PaaS) products that use OS-
level virtualization to deliver software in packages called containers
 Containers are isolated from one another and bundle their own software,
libraries and configuration files; they can communicate with each other
through well-defined channels
 Because all of the containers share the services of a single operating
system kernel, they use fewer resources than virtual machines
 Docker benefits

Consistent and Isolated Environment. Cost-effectiveness with Fast
Deployment. Mobility – Ability to Run Anywhere. Repeatability and
Automation. Test, Roll Back and Deploy. Flexibility. Collaboration, Modularity
and Scaling
 Docker components

Software, objects (container, image, service > swarms), registries
 Tools – docker CLI

Docker cmd_xyz, docker-compose, docker swarm
Publish .NET+ apps via terminal
• If you want to run or distribute your .NET+ app as an console app or exe file
(without VS) you must publish / generate an .exe via the dotnet command

Since by default only a .dll file may be created because of the portable apps model
// in a console via the dotnet exe enter the command below to run the Polymorphism project
C:\dev_folder\Polymorphism>dotnet run Polymorphism
// in a console via the dotnet exe enter the commands below to generate an exe for the Polymorphism project
C:\dev_folder\Polymorphism>dotnet publish -c Debug -r win10-x64
C:\dev_folder\Polymorphism>dotnet publish -c Release -r win10-x64

• The -r <RID> is important since it decide what target


platform the application is suited to run on

In the example above we set it to run on Windows 10 64bit
• Further and more detailed info

dotnet -h publish

https://dzone.com/articles/generate-an-exe-for-net-core-console-apps-net-core

https://stackoverflow.com/questions/44038847/vs2017-compile-netcoreapp-as-exe

https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#rid-graph
Publish .NET+ apps via GUI

1. Right click on your project and


select Publish
2. Select Target > Folder > Next
3. Select Location, adjust if
needed and press Finish
4. Adjust the profile if needed
5. Multiple settings as:
Deployment Mode: Self-
contained, Target Runtime: win-
x86 (or win-x64), … Save
6. Publish
in the folder
bin\Release\DotNetFrameWork\p
ublish\ you will find the EXE file
Recommended viewing and reading
• Unit Testing: MOQ Framework

https://channel9.msdn.com/Shows/Visual-Studio-Toolbox/Unit-Testing-Moq-Framework
• Blog post about MSTest v2

https://www.meziantou.net/2018/01/22/mstest-v2-setup-a-test-project-and-run-tests
• Moq Framework - The most popular and friendly mocking framework for .NET

https://github.com/moq/moq4
• Mocking in C# Unit tests – video and resources

https://iamtimcorey.com/mocking/
• C# Unit Tests with Mocks

http://www.patrickschadler.com/c-unit-tests-mocks/
• How YOU can Learn Dependency Injection in .NET Core and C#

https://dev.to/dotnet/how-you-can-learn-dependency-injection-in-net-core-and-c-245g
• Unit testing with XUnit and FluentAssertions in .NET Core 2 apps

http://anthonygiretti.com/2018/01/19/code-reliability-unit-testing-with-xunit-and-fluentassertions-in-net-core-2-apps/

You might also like