Tuesday, May 12, 2020

Unit Tests with Moq - 2

Mocking in .NET Core Unit Tests with Moq


Replacing the actual dependency that would be used at production time, with a test-time-only version that enables easier isolation of the code we want to test.

Why Mock?
  • Improved test execution speed
    • Slow algorithms
    • External resources: DB, Web service, etc.
  • Support parallel development streams
    • Real object not yet developed
    • Another team working on the depended object
    • External contractor
  • Improve test reliability
  • Reduce development/testing costs
    • External company bills per usage
    • Interfacing with mainframe
    • Developer effort (complexity)
  • Test when non-deterministic dependency
    • Can set return specific value return from dependency every time without failing

“Unit – it’s a situational thing – the team decides what makes sense to be a unit for the purposes of their understanding of the system and its testing” (Martin Fowler)
 

Test Doubles

“Test Double is a generic term for any case where you replace a production object for testing purposes.” (Martin Fowler)
 


  • Fake
    • Working implementation of the dependencies
    • However, Not suitable for production eg: EF Core in-memory provider
  • Dummies
    • Can be created and passed around system but they never actually used or accessed by the code been executed as part of our tests.
  • Stubs
    • Can create stubs and pass them as dependencies to code we are testing, but we can setup these stubs to provide and answers to calls made from the classes that we are testing in our test code. 
  • Mock
    • Can create mocks as dependencies to code we are testing, when we use mock, can expect calls made that mock object from the class we are testing 

Note: when we use Moq framework we can use to create Dummies, Stubs and Mocks not for Fakes
So, we will use generic term “Mock” for Dummies, Stubs and Mocks

Moq

Pronounce as “Mock-you” or “Mock”
Opensource project


Mocking Method Calls

Create, Setup return and Use Mock Object
        [Fact]
        public void DeclineLowIncomeApplications()
        {
            Mock<IFrequentFlyerNumberValidator> mockValidator =
                new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.IsValid("x")).Returns(true);
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication
            {
                GrossAnnualIncome = 19_999,
                Age = 42,
                FrequentFlyerNumber = "x"
            };
            CreditCardApplicationDecision decision = sut.Evaluate(application);
            Assert.Equal(CreditCardApplicationDecision.AutoDeclined, decision);
        }

Instated of specifying an explicit value that Mock method to expect, we can use Mock use argument matching features
Using It.IsAny() method
        [Fact]
        public void DeclineLowIncomeApplications()
        {
            Mock<IFrequentFlyerNumberValidator> mockValidator =
                new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.IsValid(It.IsAny<string>())).Returns(true);
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication
            {
                GrossAnnualIncome = 19_999,
                Age = 42
            };
            CreditCardApplicationDecision decision = sut.Evaluate(application);
            Assert.Equal(CreditCardApplicationDecision.AutoDeclined, decision);
        }

If we want to setup the return value of a mock method based on the result of some predicate we can use this using It.Is method with predicate
            mockValidator.Setup(x => x.IsValid(It.Is<string>(number => number.StartsWith('x'))))
                        .Returns(true);


Setup a mock method to return a specific value if the argument value is in within given set, using It.IsIn() method 
            mockValidator.Setup(x => x.IsValid(It.IsIn("x", "y", "z"))).Returns(true);

Rather than specifying a list we can check within range by using It.IsInRange() method
            mockValidator.Setup(x => x.IsValid(It.IsInRange("b", "z", Range.Inclusive)))
                         .Returns(true);

Also, we can match based on regular expression by using It.IsRegex() method
            mockValidator.Setup(x => x.IsValid(It.IsRegex("[a-z]",
                                System.Text.RegularExpressions.RegexOptions.None)))
                         .Returns(true);

MockBehavior

MockBehavior.Strict

Throw an exception if a mocked method is called but has not been setup

MockBehavior.Loose

Never throw exceptions, even if a mocked method is called but has not been setup

Returns default values for value types, null for reference types, empty array/enumerable/

MockBehavior.Default

Default behavior if none specified (MockBehavior.Loose)




Strict: exception will throw since no setup
            Mock<IFrequentFlyerNumberValidator> mockValidator = 
                new Mock<IFrequentFlyerNumberValidator>(MockBehavior.Strict);
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);

Loose: no exception will throw even no setup
            Mock<IFrequentFlyerNumberValidator> mockValidator = 
                new Mock<IFrequentFlyerNumberValidator>();
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);


Note: Use strict mocks only when absolutely necessary, prefer loose mocks at all other times.


Mocking Methods with “out” Parameters

setup method, rather-than chaining with .Return(true) we insteaed spesifiy the parameter created
        [Fact]
        public void DeclineLowIncomeApplicationsOutDemo()
        {
            Mock<IFrequentFlyerNumberValidator> mockValidator =
                new Mock<IFrequentFlyerNumberValidator>();
            bool isValid = true;
            mockValidator.Setup(x => x.IsValid(It.IsAny<string>(), out isValid));


Mocking Properties

Configuring a mocked property
            mockValidator.Setup(x => x.LicenseKey).Returns("EXPIRED");

Getting a rerun value from a method:
            mockValidator.Setup(x => x.LicenseKey).Returns(GetLicenseKeyExpiryString);
        string GetLicenseKeyExpiryString()
        {
            return "EXPIRED";
        }

Auto-mocking Property Hierarchy
Moq is easy to mock properties even if there nestead in a hierachy. No need to mock all object in a hierachy, In background Moq has automatically created Mock for all object in hierachy.
        [Fact]
        public void ReferWhenLicenseKeyExpired()
        {
            var mockValidator = new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.IsValid(It.IsAny<string>())).Returns(true);
            //var mockLicenseData = new Mock<ILicenseData>();
            //mockLicenseData.Setup(x => x.LicenseKey).Returns("EXPIRED");
            //var mockServiceInfo = new Mock<IServiceInformation>();
            //mockServiceInfo.Setup(x => x.License).Returns(mockLicenseData.Object);
            //mockValidator.Setup(x => x.ServiceInformation).Returns(mockServiceInfo.Object);
            mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey)
                         .Returns("EXPIRED");
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication { Age = 42 };
            CreditCardApplicationDecision decision = sut.Evaluate(application);
            Assert.Equal(CreditCardApplicationDecision.ReferredToHuman, decision);
        }

Default Value Behavior for Mock property
For value type default value will be default value of the value type, but for the reference type default value will be null. Instead of null value for reference type we can change this behavior by specifying default value behavior in Moq. (this behavior only take effect when can actually be created for that type namely it has to be Interface, abstract or non-sealed class) when we add Moq behavior for object, instated of getting null it will create mock object.
            Mock<IFrequentFlyerNumberValidator> mockValidator =
                new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.IsValid(It.IsAny<string>())).Returns(true);
            //mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey);
            mockValidator.DefaultValue = DefaultValue.Mock;

Tracking of changes to mock property value
We can configure properties to remember the changes made to them during the execution of the test

Method 1: we can setup the ValidationMode property remember all changes made to it during the execution of the test
        [Fact]
        public void UseDetailedLookupForOlderApplications()
        {
            var mockValidator = new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey).Returns("OK");
            mockValidator.SetupProperty(x => x.ValidationMode);
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication { Age = 30 };
            CreditCardApplicationDecision decision = sut.Evaluate(application);
            Assert.Equal(ValidationMode.Detailed, mockValidator.Object.ValidationMode);
        }

Method 2: If we have lots of properties on the Mock object, we want to setup, rather than setup them individually, we can instead call SetupAllProperties method. Now all the properties in mock object have checking enabled. Call SetupAllProperties before any specific Setup to prevent override the specific setups.
        [Fact]
        public void UseDetailedLookupForOlderApplications()
        {
            var mockValidator = new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.SetupAllProperties();
            mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey).Returns("OK");
            //mockValidator.SetupProperty(x => x.ValidationMode);
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication { Age = 30 };
            CreditCardApplicationDecision decision = sut.Evaluate(application);
            Assert.Equal(ValidationMode.Detailed, mockValidator.Object.ValidationMode);
        }


Behavior Testing and State Based Testing

State based testing



Behavior based testing


Implementing Behavior Verification Tests
When the thing we are trying to test doesn’t expose any useful state to allow us that it is working as expected.

Verifying a Method was called
        [Fact]
        public void ShouldValidateFrequentFlyerNumberForLowIncomeApplications()
        {
            var mockValidator = new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey).Returns("OK");
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication { FrequentFlyerNumber = "q" };
            sut.Evaluate(application);
            mockValidator.Verify(x => x.IsValid(It.IsAny<string>()));
        }

Adding a Custom Error Message
            mockValidator.Verify(x => x.IsValid(It.IsNotNull<string>()),
                "Frequent flyer number passed should not be null");
       

Verifying a Method was not called

            mockValidator.Verify(x => x.IsValid(It.IsAny<string>()), Times.Never);

Verifying how many times a Method was called 
            mockValidator.Verify(x => x.IsValid(It.IsAny<string>()), Times.Once);

            mockValidator.Verify(x => x.IsValid(It.IsAny<string>()), Times.Exactly(1));

            mockValidator.Verify(x => x.IsValid(It.IsAny<string>()), Times.AtLeastOnce());

            mockValidator.Verify(x => x.IsValid(It.IsAny<string>()), Times.AtMostOnce());

            mockValidator.Verify(x => x.IsValid(It.IsAny<string>()), Times.AtMost(1));

Verifying a Property Getter was called
            mockValidator.VerifyGet(x => x.ServiceInformation.License.LicenseKey, Times.Once);

Verifying a Property Setter was called
            mockValidator.VerifySet(x => x.ValidationMode = It.IsAny<ValidationMode>(), Times.Once);


Using Additional Mocking Techniques


Throwing Exceptions from Mock objects

Generic Exception:
            mockValidator.Setup(x => x.IsValid(It.IsAny<string>()))
                         .Throws<Exception>();

Specific Exception
            mockValidator.Setup(x => x.IsValid(It.IsAny<string>()))
                         .Throws(new Exception("Custom message"));


Raising Events from Mock Object

Raising manually:
        [Fact]
        public void IncrementLookupCount()
        {
            Mock<IFrequentFlyerNumberValidator> mockValidator = new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey).Returns("OK");
            mockValidator.Setup(x => x.IsValid(It.IsAny<string>())).Returns(true);
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication { FrequentFlyerNumber = "x", Age = 25 };
            //sut.Evaluate(application);
            mockValidator.Raise(x => x.ValidatorLookupPerformed += null, EventArgs.Empty);
            Assert.Equal(1, sut.ValidatorLookupCount);
        }

Raising automatically:
        [Fact]
        public void IncrementLookupCount()
        {
            Mock<IFrequentFlyerNumberValidator> mockValidator = new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey).Returns("OK");
            mockValidator.Setup(x => x.IsValid(It.IsAny<string>()))
                         .Returns(true)
                         .Raises(x => x.ValidatorLookupPerformed += null, EventArgs.Empty);
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication { FrequentFlyerNumber = "x", Age = 25 };
            sut.Evaluate(application);
            mockValidator.Raise(x => x.ValidatorLookupPerformed += null, EventArgs.Empty);
            Assert.Equal(1, sut.ValidatorLookupCount);
        }

Returning Different Results for Sequential Calls 
SetupSequence – allows to do is chain multiple return methods for the setup
        [Fact]
        public void ReferInvalidFrequentFlyerApplications_Sequence()
        {
            Mock<IFrequentFlyerNumberValidator> mockValidator = new Mock<IFrequentFlyerNumberValidator>();
            mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey).Returns("OK");
            mockValidator.SetupSequence(x => x.IsValid(It.IsAny<string>()))
                         .Returns(false)
                         .Returns(true);
            var sut = new CreditCardApplicationEvaluator(mockValidator.Object);
            var application = new CreditCardApplication { Age = 25 };
            CreditCardApplicationDecision firstDecision = sut.Evaluate(application);
            Assert.Equal(CreditCardApplicationDecision.ReferredToHuman, firstDecision);
            CreditCardApplicationDecision secondDecision = sut.Evaluate(application);
            Assert.Equal(CreditCardApplicationDecision.AutoDeclined, secondDecision);
        }

Mocking Members of Concrete Types
Mark the member as Virtual to access

Mocking Virtual Protected Members
            mockFraudLookup.Protected()
.Setup<bool>("CheckApplication", ItExpr.IsAny<CreditCardApplication>())
.Returns(true);

Improving Mock Setup Readability with LINQ to Mocks
        [Fact]
        public void LinqToMocks()
        {
            //Mock<IFrequentFlyerNumberValidator> mockValidator = new Mock<IFrequentFlyerNumberValidator>();
            //mockValidator.Setup(x => x.ServiceInformation.License.LicenseKey).Returns("OK");
            //mockValidator.Setup(x => x.IsValid(It.IsAny<string>())).Returns(true);
            IFrequentFlyerNumberValidator mockValidator
                = Mock.Of<IFrequentFlyerNumberValidator>
                (
                    validator =>
                    validator.ServiceInformation.License.LicenseKey == "OK" &&
                    validator.IsValid(It.IsAny<string>()) == true
                );
                
            var sut = new CreditCardApplicationEvaluator(mockValidator);
            var application = new CreditCardApplication { Age = 25 };
            CreditCardApplicationDecision decision = sut.Evaluate(application);
            Assert.Equal(CreditCardApplicationDecision.AutoDeclined, decision);
        }

Read more ...

Unit Tests with Moq - 1

Unit Tests

Why write automated tests?
  • Help to find defects and regressions
  • Free to run as often as required
  • Run at any time, on-demand or scheduled
  • Quicker to execute than manual testing
  • Find errors sooner
  • Generally reliable
  • Test code sites with production code
  • Happier development teams

Different test types

Depth – how details the tests can potentially get
Breadth – how broad the test is or how many difference sub systems of the application we are developing, the test can potentially cover
Speed – Tests execution speed
Brittle – how easy the test can be break or require refactoring



Unit Tests

May test a single class or unit of functionally cohesive classes. Unit test offers a best depth of testing and we can get details behavior of a class and check it all working as expected. But unit test doesn’t have broad reach, so unit test wouldn’t cover the whole system stack, right from the user interface down to the database
An “unit test” test a class or multiple classes without their external dependencies. They test unit of work.Unit test are cheap to write and execute fast. In this way, we can test each building block working as expected. However, this is not testing with external dependencies you can’t get lot of confidence in the reliability of your application. That the way Integration Test coming to rescue.


Integration Tests

Potential less in depth than Unit tests, but they are cover greater breadth of the system. Integration test might test sub-systems working together and potentially external resources such as database or the file systems
An “integration test” test a class or multiple classes with their external dependencies. 
This tests integration of your application code with these concrete dependencies like files, databases, so on. These tests take longer to execute, because they often reading and writing to external dependencies. It gives more confidence in the health of your application.


Subcutaneous Tests

Operate just under the User interface of the application. This means that this can cover potentially great breadth of the system we are developing but on in depth as Unit test
This is particularly valuable when doing functional testing of an application: when you want to test end-to-end behavior, but it's difficult to test through the UI itself. 


User Interface Tests

Can cover greatest breadth of the application.




The logical phases of an automated test







Asserts

Evaluate and verity the outcome of a test. Based on a returned result, final object state, or the occurrence of events observed during execution. An assert can either pass or fail. If all asserts pass, the test passes; if any assert fails the test fails.


How many asserts per test?
We can have more than one Assert in an individual test method as long as those asserts are testing the same behavior.



Type of xUnit Asserts: 

 

 xUnit Asserts types

 Example

Boolean

Assert.True(actualValue)

Assert.True(sut.Value)

String

 

Assert.Equal(“expected”, “actual”)

Assert.Equal(“abc”, “sut.Value”)

Assert.Equal(“expected”, “actual”, ignoreCase: true)

Assert.Equal(“ABC”, “sut.Value”, ignoreCase: true)

Assert.StartWith(“expected”, “actual”)

Assert.StartWith(“abc”, “sut.Value”)

Assert.EndWith(“expected”, “actual”)

Assert.EndWith(“abc”, “sut.Value”)

Assert.Matches(“expectedRegExp”, “actual”)

Assert.Matches(“[A-Z]”, “sut.Value”)

Number

Assert.Equal(expected, actual)

Assert.Equal(123, sut.Value)

Assert.NotEqual(expected, actual)

Assert.NotEqual(123, sut.Value)

Assert.True(numberComparison)

Assert.True(sut.Value >= 123 && sut.Value <= 321)

Assert.InRange(accrual, low, high)

Assert.InRange(sut.Value, 123, 321)

Assert.Equal(expected, actual, precision)

Assert.Equal(123.667, sut.Value, 3)

Null

Assert.Null(actual) 

Assert.Null(sut.Value) 

Assert.NotNull(actual)

Assert.NotNull(sut.Value)

Collection

Assert.Contains(expectedElement, actualCollection) 

Assert.Contains(“a”, sut. Collection) 

Assert.DoesNotContain(expectedElement, actualCollection) 

Assert.DoesNotContain(“a”, sut. Collection) 

Assert.Contains(actualCollection, expectedPredicate) 

Assert.Contains(sut.Collection, collection=> collection.Contains(“abc”) 

Assert.Contains(expectedCollection, actualCollection)

Assert.Contains(collection, sut.Collection)

Assert against each item in the collection, “All” will loop through the collection

Assert.All(sut.Collection, s => Assert.False(s))

Object

Check the type of object

Assert.IsType<ClassA>(obj)

Check the type of object is not

Assert.IsNotType<DateTime>(obj)

Check the type for inherited class

Assert.IsAssignableFrom<ClassA>(obj)

Object instances

Check object references are same

Assert.Same(obje1, obj2)

Check object references are not same

Assert.NotSame(obje1, obj2)

Throws Exception

Assert.Throws<expectedException, callingMethod)

Assert.Throws<ArgumentNullException>(() => sut.Create(null))

Particular argument causes the error and throws exception

Assert.Throws< ArgumentNullException>(“name”, () => sut.Create(null)

Event Raised

Asserting that events are raised

Assert.Raises<EventArgs>(handler => sut.Event += handler, handler => sut.Event -= handler, () => sut.Method());

Asserting Property change event

Assert.PropertyChanged(sut, “Health”, () => sut.TakeDamage(10))




Controlling Test Execution

Categorizing and Running sub tests

Set category method level:
public class BossEnemyShould
{
[Fact]
[Trait("Category", "Boss")]
public void HaveCorrectPower()
{
BossEnemy sut = new BossEnemy();
Assert.Equal(166.667, sut.SpecialAttackPower, 3);
}
}
Set category class level:
    [Trait("Category", "Enemy")]
    public class EnemyFactoryShould
    {
        [Fact]        
        public void CreateNormalEnemyByDefault()
        {
   
Search:
Trait:”Enemy”

Run all tests from dotnet core:
>dotnet test
Run tests for specific Trait from dotnet core:
>dotnet test –filter TraitName=TraitValue
>dotnet test --filter Category=Enemy
>dotnet test --filter “Category=Boss|Category=Enemy”

Skip test
[Fact(Skip = "Don't need to run this")] 
public void CreateNormalEnemyByDefault()
{

xUnit Setup and Dispose

In xUnit we can do setup in constructor, constructor will execute before each test case run. So, we can create any initialization on the constructor
Also, we can implement the IDisposable interface to dispose any resources. Dispose method will execute after each test case run.
public class PlayerCharacterShould : IDisposable
    {
        private readonly PlayerCharacter _sut;
        private readonly ITestOutputHelper _output;
        public PlayerCharacterShould(ITestOutputHelper output)
        {
            _output = output;
            _output.WriteLine("Creating new PlayerCharacter");
            _sut = new PlayerCharacter();
        }
        public void Dispose()
        {
            _output.WriteLine($"Disposing PlayerCharacter {_sut.FullName}");
            _sut.Dispose();
        }
        [Fact]
        public void BeInexperiencedWhenNew()
        {

Share Context between Tests methods in a test class

Create once and share the instance among all the test methods in the test class. For this we will create a new class and implement this class in the test class using IClassFiture
   public class GameStateFixture : IDisposable
    {
        public GameState State { get; private set; }
        public GameStateFixture()
        {
            State = new GameState();
        }
        public void Dispose()
        {
            // Cleanup
        }
    }

public class GameStateShould : IClassFixture<GameStateFixture>
    {
        private readonly GameStateFixture _gameStateFixture;
        private readonly ITestOutputHelper _output;
        public GameStateShould(GameStateFixture gameStateFixture,
                               ITestOutputHelper output)
        {
            _gameStateFixture = gameStateFixture;
            _output = output;
        }
        [Fact]
        public void DamageAllPlayersWhenEarthquake()
        {
            _output.WriteLine($"GameState ID={_gameStateFixture.State.Id}");
            var player1 = new PlayerCharacter();
            var player2 = new PlayerCharacter();
            _gameStateFixture.State.Players.Add(player1);

Share Context across Test Classes

If we want to share a context across multiple test classes. Doesn’t have any implementation in collection class, use to create the collection definition
    [CollectionDefinition("GameState collection")]
public class GameStateCollection : ICollectionFixture<GameStateFixture> {}
    [Collection("GameState collection")]
    public class TestClass1
    {
        private readonly GameStateFixture _gameStateFixture;
        private readonly ITestOutputHelper _output;
        public TestClass1(GameStateFixture gameStateFixture, ITestOutputHelper output)
        {
            _gameStateFixture = gameStateFixture;
            _output = output;
        }
        [Fact]
        public void Test1()
        {
So, from above configuration, before any of tests executes, xUnit going to create an instance of fixture class and supply same instance to test classes which have collection attribute.


Data-driven Tests


Will be use [Theory] attribute to mark the test going to be use multiple times
Pass parameters to test method to use inside method
Then use one on Test Data Source to pass values for parameters

Test Data Sources
  • Inline attribute
  • Property / field / method
  • Custom attribute
  • External data
If we want to share test data across Tests, we can use above Test data sources except Inline attribute. Inline attribute can’t share data amount test methods, it specific to the method it defines. Also, Except External data other three methods had to involve a developer and need to do code changes to add additional test data. But in the External data can be edit by non developer save in external file

Inline attribute
        [Theory]
        [InlineData(0, 100)]
        [InlineData(1, 99)]
        [InlineData(50, 50)]
        public void TakeDamage(int damage, int expectedHealth)
        {

Property / field / method

First save test data in a separate class and write a get method to get all test data
public class InternalHealthDamageTestData
    {
        //private static readonly List<object[]> Data = new List<object[]>
        //     {
        //            new object[] {0, 100},
        //            new object[] {1, 99},
        //            new object[] {50, 50},
        //            new object[] {101, 1}
        //     };
        //public static IEnumerable<object[]> TestData => Data;
        public static IEnumerable<object[]> TestData
        {
            get
            {
                yield return new object[] { 0, 100 };
                yield return new object[] { 1, 99 };
                yield return new object[] { 50, 50 };
                yield return new object[] { 75, 25 };
                yield return new object[] { 101, 1 };
            }
        }
    }
Add [MemberData] attribute to pass the test data to any test method
        [Theory]
        [MemberData(nameof(InternalHealthDamageTestData.TestData), 
            MemberType = typeof(InternalHealthDamageTestData))]
        public void TakeDamage(int damage, int expectedHealth)
        {
            NonPlayerCharacter sut = new NonPlayerCharacter();
            sut.TakeDamage(damage);
            Assert.Equal(expectedHealth, sut.Health);
        }

Getting Test Data from External file
Add a csv file to solution and in the properties change the “Copy to Output Directory” to “Copy always”
public class ExternalHealthDamageTestData
    {
        public static IEnumerable<object[]> TestData
        {
            get
            {
                string[] csvLines = File.ReadAllLines("TestData.csv");
                var testCases = new List<object[]>();
                foreach (var csvLine in csvLines)
                {
                    IEnumerable<int> values = csvLine.Split(',').Select(int.Parse);
                    object[] testCase = values.Cast<object>().ToArray();
                    testCases.Add(testCase);
                }
                return testCases;
            }
        }
    }
        [Theory]
        [MemberData(nameof(ExternalHealthDamageTestData.TestData), 
            MemberType = typeof(ExternalHealthDamageTestData))]
        public void TakeDamage(int damage, int expectedHealth)
        {
            NonPlayerCharacter sut = new NonPlayerCharacter();
            sut.TakeDamage(damage);
            Assert.Equal(expectedHealth, sut.Health);
        }

Create Custom Attribute as the Data Source
        [Theory]
        [HealthDamageData]
        public void TakeDamage(int damage, int expectedHealth)
        {
            NonPlayerCharacter sut = new NonPlayerCharacter();
            sut.TakeDamage(damage);
            Assert.Equal(expectedHealth, sut.Health);
        }
Method 1:
    public class HealthDamageDataAttribute : DataAttribute
    {
        public override IEnumerable<object[]> GetData(MethodInfo testMethod)
        {
            yield return new object[] { 0, 100 };
            yield return new object[] { 1, 99 };
            yield return new object[] { 50, 50 };
            yield return new object[] { 75, 25 };
            yield return new object[] { 101, 1 };
        }
    }


Method 2:
    public class HealthDamageDataAttribute : DataAttribute
    {
        public override IEnumerable<object[]> GetData(MethodInfo testMethod)
        {
            string[] csvLines = File.ReadAllLines("TestData.csv");
            var testCases = new List<object[]>();
            foreach (var csvLine in csvLines)
            {
                IEnumerable<int> values = csvLine.Split(',').Select(int.Parse);
                object[] testCase = values.Cast<object>().ToArray();
                testCases.Add(testCase);
            }
            return testCases;
        }
    }

Continue reading: Unit Tests with Moq - 2
Read more ...

Thursday, May 7, 2020

Clean Architecture

Architecture means the overall design of the project. Robert C. Martin (aka Uncle Bob) deeply developed his vision of a clean architecture in his book, that I highly recommend.
The main points:
  • Strong separation of Concerns
  • Independent of frameworks
  • Independent of UI
  • Independent of database
  • Testable

Aim to produce the systems that are highly independent of frameworks, User Interface, databases and testable, so you should be able to model the core of your application and test it without any UI, Database or any Frameworks. The way Clean Architecture is differed from Traditional Layered Architecture is the direction of the dependencies.


Traditional "N-Layer" architecture applications




In traditional layered Architecture we have Presentation Layer, which is depended on Business Logic Layer (BLL), which is dependent on Data Access Layer(DAL)

By applying Dependency Inversion Principle of Object Orientation, between BLL and DAL will end up with Architecture like bellow figure.



Clean architecture


So instead of BLL depend on DAL, DAL depend on BLL. Now the BLL has no dependencies to Presentation or DAL. It completely isolated from any presentation or persistence frameworks. In this Layer we can implement all use cases and business rules and test them no matter what presentation or persistence framework we going to use.  And more importantly we can swap any framework in the future for Presentation or Persistence, but Core of the application is not dependent on these frameworks.


Clean architecture (Onion view)










Dependency Inversion Principe (DIP)

There are two parts on the definition.
A:
"High-level modules should not depend on low-level modules. Both should depend on abstractions".


In above example controller is high level module and UnitOfWork is low level module. According to above figure controller dependent or tightly couple to UnitOfWork, which means we are violating the Dependency Inversion Principle. Issue here is if we change UnitOfWork the controller may have to be change, or at least compile assemble in which defined and need to be redeploy. DIP help us to remove this coupling, so instead of depend on UnitOfWork it will dependent on abstraction/Interface that will be implemented by UnitOfWork, if we change this implementation controller will not be effected as long as contracted stays the same.



B:
"Abstractions should not depend on details. Details should depend on abstractions".

Complete()
Product: ProductRepository()

In above example our Interface/abstraction is dependent on ProductRepository which is details or concrete implementation.  Which is tightly couple to dbcontext and Entity Framework(EF). So, this Interface is leaky and it’s not good abstraction which means controller is depended on IUnitOfWork even though this is an interface the controller still will be tightly couple to EF. DIP help us to remove this coupling by replacing this repository with an abstraction like IProductRepository. That knows absolutely 

Complete()
Product: IProductRepository()




The Repository Pattern

"Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects". (PoEAA – Martin Fowler)
  • Decouple business code from data access code
    If you use Entity framework and in future if you want to switch to different ORM, you can do so with minimal impact on the rest of application.
  • Provides better separation of concerns
    Controller no longer responsible for querying, querying is data access concern, it’s not the responsibility of a controller, controller is responsible for application flow, so controller receive requests and generate responses.
  • Minimize duplicate query logic
    If we have lots of controllers all using same repository, we can re-use this without duplicate same query logic inside all controllers
  • Testability
    Much easier to create a Mock of repository than is to create a Mock of dbcontext. dbcontext class is big. It carries lots of methods with it and it’s kind of tricky to test if our code using dbcontext inside API controllers


Q. If you use Entity framework and it already decoupled, and Repository pattern is already implemented in EF, So why do we need to do this again?

Reason for this if we have lots of controllers all using same repository, we have to duplicate query logic inside all controllers.

Some Consequences adding Repository Pattern

  • Increase level of abstraction
    Can be a good thing or bad thing. We don’t want too many levels abstractions, but in this case,  we are abstracting away from database with EF, abstracting away from EF with the repository, repository sit between dbcontext and controller, so in this case fine.
  • Increased maintainability, flexibility, testability
    All good things. It easier to maintain our repositories that is to maintain controllers with dbcontect injected into the controllers
  • More classes/Interfaces but less duplicate code
  • Business logic move further away from the data
    This is good thing. We don’t want to our business logic tight too closely to our data, as then it makes too difficult to move away from particular database technology or ORM technology
  • Harder to optimize certain operations against the data source
    If we want specific queries like return only few fields, RP harder to optimize these queries, what RP do is get entity as whole then shape it after we got data back inside memory. But we can use another pattern on top of this call “Specification Pattern” to achieve this.


inject our repository into the controller, then the controller is going to see what's available inside the repository to use. All of a sudden, controller becomes a lot easier to manage. It's more lightweight and the code is cleaner. Repository has access to the dbcontext and that's responsible for the _context .Products list method. And then Context is going to translate this query into a database query and say “select * from products” and that's going to get passed back up the chain to the controller which would then return those results to the client that was requesting it.


Generic Repository

  • When our application growing and have more entities, it is pointless to create repositories for each and every entity,
  • The Idea behind the “Generic Repository” is that we just have single Repository that can be use with any number of Entities and we don’t want to create any more repositories. We can just use the one Generic Repositories for all Entities.
  • Generic Repository can sometimes have bad reputation, and that because how developers used in the past to get around the problem that a generic repository is just that it's generic and not all of your data access requests are going to be the same, some entities you want to bring back paginated responses, sometimes you want to filter them or sort them in a certain way. When we use a generic repository, this makes it difficult to achieve. So, the way to get around this is to use the specification pattern. So, it kind of comes hand in hand when we use a generic repository and we'll be taking a look at that as well.

Generics:

  • Been around since C# 2.0 (2002)
  • Help avoid duplicate code
  • Type safety
  • We mostly use generics rather than create them


Specification Pattern

Specification Pattern can be anti-pattern if you use it incorrectly. 
Specification pattern is clear and flexible, It gives us many ways to query our data and return from our database.
  • Describe a query in an object
    So instead of just passing an expression to a method for example we could define a specification.
  • Object which contains the query we want to send to that particular method, and it returns an IQueryable<T> which is really an expression tree that we’re going to use to send to our method. That’s going to execute the query on our database. 
  • So, our generic method would take the specification as a parameter instead of a generic expression as a parameter and our specification can then have a meaningful name.


Unit of Work Pattern

Maintains a list of objects affected by a business transaction and coordinates the writing out of changes. (PoEAA – Martin Fowler)


If we have multiple repositories is the potential to end up with partial updates 
Partial update: each repository owns version of the dbcontext which is used. So that we can not just query our data from the database but also add updates and remove entities from the database as well. If each repository has its own version of the dbcontext then one thing could succeed when we save the changes, and one thing could fail in a different repository, that would leave us ending up partial update



Responsibilities of UoW

  • Creates dbcontext instance
  • Creates Repositories as needed by sharing same dbcontext
  • Inside UoW we can create transactions, until we call Complete() method in UoW changes not apply to the database. When we call Complete() method it will save all into the database or it will save non of the changes into the database(roll back to where we were before).
Without UoW - each repository owns version of the dbcontex



Unit of Work
Read more ...

Powered By Blogger |   Design By Seo Blogger Templates Published.. Blogger Templates
DMCA.com