From 0ecd6620865b4234247b76a4d75f6b1204ccf7e2 Mon Sep 17 00:00:00 2001 From: borb Date: Sun, 13 Jul 2025 16:39:16 +0300 Subject: [PATCH] finish lecture 8 --- 8-testing-slides.html | 957 +++++++++++++++++++++++++++++++ 8-testing.md | 1271 ++++++++++++++++++++--------------------- imgs/9-testing_6.png | Bin 46631 -> 41191 bytes 3 files changed, 1591 insertions(+), 637 deletions(-) create mode 100644 8-testing-slides.html diff --git a/8-testing-slides.html b/8-testing-slides.html new file mode 100644 index 0000000..e079d18 --- /dev/null +++ b/8-testing-slides.html @@ -0,0 +1,957 @@ +8. Testing
+

Testing

+
+
+

Contents

+ +
+
+

Introduction to testing

+
+
+

What is testing?

+
    +
  • Testing is much more than just trying out manually if the program works!
  • +
  • Manual testing: acting as an end user of the application
  • +
  • Automated testing: using software to emulate the end user, or testing the functionality of the code itself
  • +
  • Proving that the program is working as intended during development on a... +
      +
    • ...micro level: individual pieces of code, such as methods
    • +
    • ...macro level: larger functional components, such as services
    • +
    +
  • +
  • All possible paths in a piece of code should be tested + +
  • +
+
+
+

Automated testing: Why?

+
    +
  • Why do automated testing? +
      +
    • Sets ground truths that can be relied on during development: +
        +
      • "With this kind of input, this should happen"
      • +
      • (No need to take the programmers word for it)
      • +
      +
    • +
    • Ensures code functionality before moving on +
        +
      • Ideally finding bugs before starting to debug
      • +
      +
    • +
    • Guides developers to create more testable and thus more readable, interchangeable and overall better code
    • +
    • Test-driven development (TDD) is based on writing tests first and code later!
    • +
    +
  • +
+
+
+

Types of automated testing

+
    +
  • Unit testing +
      +
    • Testing individual units (methods, classes) within a project
    • +
    • "input x produces output y"
    • +
    • Each unit has to be entirely independent and deterministic for it to be unit testable! +
        +
      • Deterministic: Always the same result with the same input
      • +
      +
    • +
    +
  • +
  • Integration testing +
      +
    • Testing complete modules and the interaction between them within a project
    • +
    • Closer to real life scenarios
    • +
    +
  • +
  • End-to-end (E2E) testing +
      +
    • Testing the whole program by emulating the end user's behaviour
    • +
    +
  • +
+
+
+

Test type comparison

+
    +
  • Unit tests are faster to write, E2E tests slowest
  • +
+
+

+
+
+
+

C# Testing Frameworks

+
    +
  • Three main testing frameworks: +
      +
    • MSTest
    • +
    • NUnit
    • +
    • XUnit
    • +
    +
  • +
  • You can install these using NuGet, or in the newer versions of Visual Studio you can create a new testing project
  • +
  • NUnit is the most popular of the three, and will be used in this lecture
  • +
+
+
+

MSTest

+
    +
  • Built-in to most versions of Visual Studio since 2005
  • +
  • Simple to use, easy structure +
      +
    • TestClass for denoting classes containing unit tests
    • +
    • TestMethod for denoting unit test methods
    • +
    +
  • +
+
+
+

NUnit

+
    +
  • Most popular C# unit testing framework
  • +
  • Very similar structure to MSTest tests +
      +
    • Instead of TestClass, we use TestFixture
    • +
    • Instead of TestMethod, we use Test / TestCase
    • +
    +
  • +
  • Has some advantages to MSTest or XUnit tests, for example: +
      +
    • Ability to test a single test method using multiple different arguments
    • +
    +
  • +
+
+
+

XUnit

+
    +
  • Free, open-source unit testing tool for .NET framework and .NET core
  • +
  • Made by the creator of NUnit v2
  • +
  • You can test C#, F#, VB.NET and other .NET languages
  • +
  • Part of the .NET Foundation
  • +
  • Unique and different style
  • +
+
+
+

Note about test case naming

+
    +
  • In C# unit testing, the most commonly followed naming convention for test cases/methods is as follows:
    FunctionNameThatIsTested_Condition_ShouldDoX
    +
    +
  • +
  • For example:
    Fibonacci_WhenNIs0_ShouldReturn0
    +
    +
  • +
+
+
+

An extensive unit test Example

+
+
+

Creating a solution & sample project

+

First, we need a solution and a sample project.

+
+
+
    +
  1. In Visual Studio, create a Class library that targets .NET Standard.
  2. +
  3. Name the project Fibonacci.Library and the solution Fibonacci. Remember to uncheck Place solution and project in the same directory if it isn't unchecked already.
  4. +
+
+
+

+
+
+
+
+

Adding the function to be tested

+
+
+
    +
  1. Rename the default Class1.cs file to Fibonacci.cs.
  2. +
  3. Allow Visual Studio to also rename the class itself to Fibonacci. +
      +
    • Make the class public and static.
    • +
    +
  4. +
  5. Add a recursive fibonacci function shown here.
  6. +
+
+
+
using System;
+
+namespace Fibonacci.Library
+{
+    public static class Fibonacci
+    {
+        public static long Recursive(
+            int n,
+            long previous = 0,
+            long current = 0,
+            int counter = 0
+        )
+        {
+            return counter == n ?
+                previous + current :
+                Recursive(
+                  n,
+                  current,
+                  Math.Max(previous + current, 1),
+                  counter + 1
+                );
+        }
+    }
+}
+
+
+
+
+
+

Adding a testing project

+
    +
  • Now we need to add a testing project to our solution.
  • +
+
    +
  1. Right click your solution, and add a new project. Select the NUnit project for .NET Core, and call it Fibonacci.UnitTests.
  2. +
  3. Rename the created class file to Fibonacci_Tests.cs, and the class to Fibonacci_Tests
  4. +
+
+
+

Linking the testing project

+
    +
  1. We then need to link the Fibonacci.Library project to Fibonacci.UnitTests.
  2. +
  3. Right click Fibonacci.UnitTests, and select Add > Reference..., and under Projects select Fibonacci.Library. Press OK.
  4. +
+
    +
  • Now we have linked Fibonacci.Library to Fibonacci.UnitTests, and we can begin testing our Recursive function in the Fibonacci class.
  • +
+
+
+

Importing the class

+
    +
  1. Since we have many things called "Fibonacci", we have to import our Fibonacci class by using an alias +
      +
    • Put this to the top of your file Fibonacci_Tests.cs, underneath
      using NUnit.Framework;
      using FibonacciLib = Fibonacci.Library.Fibonacci;
      +
      +
    • +
    +
  2. +
+
+
+

Test fixture for a class

+
    +
  1. Now, add [TestFixture] on top of our class, like so:
  2. +
+
using NUnit.Framework;
+using FibonacciLib = Fibonacci.Library.Fibonacci;
+
+namespace Fibonacci.UnitTests {
+    [TestFixture]
+    public class Fibonacci_Tests {
+
+
    +
  • This denotes that this class contains our unit tests.
  • +
+
+
+

Adding the unit test method

+
    +
  1. Then, inside our class Fibonacci_Tests, let's create our first method to test the Recursive function, as shown here:
    using NUnit.Framework;
    +using FibonacciLib = Fibonacci.Library.Fibonacci;
    +
    +namespace Fibonacci.UnitTests {
    +    [TestFixture]
    +    public class Fibonacci_Tests {
    +        [Test]
    +        public void Recursive_WhenNIs0_ShouldReturn0()
    +            => Assert.That(0, Is.EqualTo(FibonacciLib.Recursive(n: 0)));
    +    }
    +}
    +
    +
  2. +
+
+
+

Things to take into consideration

+
    +
  • When passing in function arguments, to avoid magic numbers and to improve code readability, use the syntax argumentName: value +
      +
    • Magic numbers = using numbers as input as-is without explanation, for example without assigning to a well-named variable, or without using a descriptive type
    • +
    +
  • +
  • Assert.That is one of many functions that can determine whether the test passes +
      +
    • Its first argument is the expected value. In the second argument you give a constraint for the actual output, i.e., value is equal to, greater than, starts with, etc.
    • +
    • Do not mix the arguments up! If you do, the errors you get do not make any sense.
    • +
    +
  • +
+
+
+

Running Tests

+
    +
  • +

    Now, let's open the built-in test runner in Visual Studio to run our tests.

    +
  • +
  • +

    On the top of Visual Studio, click Test, then Test Explorer.

    +
  • +
  • +

    In the Test Explorer, you should see the test case we created. If not, build the solution, and make sure there are no errors.

    +
  • +
  • +

    Either way, let's run our test case. Press Run All Tests in the top-left corner of Test Explorer. You should see our test case passing.

    +
  • +
+
+
+

Adding More Test Cases

+
    +
  • While there's nothing inherently wrong about our test case, it doesn't cover much of anything.
  • +
  • We need to add more test cases to test different values and outputs.
  • +
  • In other testing frameworks, you would do this by creating more methods.
  • +
  • But with NUnit, we can use our existing method.
  • +
+
+
+

Adding a new test

+
    +
  1. Do the following modifications to our test method:
    [TestCase(0, 0)]
    +public void Recursive_Always_ShouldReturnNthFibonacci(
    +    int n,
    +    long expected
    +)
    +    => Assert.That(expected, Is.EqualTo(FibonacciLib.Recursive(n)));
    +
    +
  2. +
+
    +
  • Using TestCase instead of Test, we can pass in arguments to our test case method, and we can use those passed in arguments in our actual tests.
  • +
+
+
+

Adding more tests

+
    +
  1. +

    Run the test cases again, and they should pass.

    +
      +
    • But as you might notice, we haven't really changed anything yet.
    • +
    +
  2. +
  3. +

    Let's add more test cases, like so:

    +
    [TestCase(0, 0)]
    +[TestCase(1, 1)]
    +public void Recursive_Always_ShouldReturnNthFibonacci(
    +
    +
  4. +
  5. +

    Now we have 2 test cases with only one test method. Run the tests again, and both cases should pass.

    +
  6. +
+
+
+

Adding a failing test

+
    +
  1. Let's add one more TestCase, like so:
    [TestCase(2, 1)]
    +
    +
  2. +
  3. Run the tests again, and you should see the test case fail. +
      +
    • This is to be expected: the 2nd fibonacci number should be 1, but the actual value we got from our function was 2.
    • +
    • This means our function does not work as expected.
    • +
    +
  4. +
+
+
+

Fixing our code

+
+
+
    +
  1. Let's fix our function, as seen here.
  2. +
  3. Run the tests again. You should see all pass now.
  4. +
  5. Add a few more test cases +
      +
    • (If you don't know the fibonacci sequence, you can check it from the internet.)
    • +
    +
  6. +
+
+
+
public static long Recursive(
+    int n,
+    long previous = 0,
+    long current = 0,
+    int counter = 0
+)
+{
+    return counter == n ?
+        current : // changed from previous + current
+        Recursive(
+          n,
+          current,
+          Math.Max(previous + current, 1),
+          counter + 1
+        );
+}
+
+
+
+
+
+

Exercise 1: Unit testing in practice

+ +

Answer the following questions:

+
    +
  1. How many test cases should we ideally have to test our Recursive function?
  2. +
  3. What happens if we pass in n = int.MaxValue (2147483647)? +
      +
    • What about other huge numbers?
    • +
    • How would you fix the function so it works as expected?
    • +
    +
  4. +
  5. What happens if we pass in n = -1? +
      +
    • What about other negative numbers?
    • +
    • How would you fix the function so it works as expected?
    • +
    +
  6. +
+
+
+

Answers to Exercise 1: Unit testing in practice

+ +
    +
  1. We should cover all the edge cases and code paths with our test cases. We do not need to have many test cases for the same code path.
  2. +
  3. Stack Overflow, Int64 Overflow, or the test case just won't run. +
      +
    • We should have a maximum allowed value for n, throw the argument out of range exception if above
    • +
    +
  4. +
  5. Stack Overflow, since current can never equal n that is less than 0 +
      +
    • We should have a minimum allowed value for n, throw the argument out of range exception if below
    • +
    +
  6. +
+
+
+

Testing exceptions

+
    +
  • To expect exceptions to be thrown in test cases, we can use the method Assert.Throws
  • +
  • Syntax is as follows:
    Assert.Throws<ExceptionType>(delegate);
    +
    +
  • +
  • In practice:
    Assert.Throws<NullReferenceException>(() => TestedFunction(n));
    +
    +
  • +
+
+
+

Handling border cases

+
+
+
    +
  • Continuing our Fibonacci test case, we need to add handling for n that is less than 0 or more than a set amount. Then we need to add another test method and two test cases to test our new functionality.
  • +
+
    +
  1. Let's start with modifying our existing Recursive function, as shown here:
  2. +
+
+
+
public const int MinDepth = 0;
+public const int MaxDepth = 10000;
+
+public static long Recursive(
+    int n,
+    long previous = 0,
+    long current = 0,
+    int counter = 0
+)
+{
+    if (n < MinDepth || n > MaxDepth)
+        throw new ArgumentOutOfRangeException(
+          "n is out of bounds!"
+        );
+    
+    return counter == n ?
+        current :
+        Recursive(
+          n,
+          current,
+          Math.Max(previous + current, 1),
+          counter + 1
+        );
+}
+
+
+
+
+
+

Testing that everything works

+
    +
  • Now our Recursive function throws an ArgumentOutOfRange exception when n is less than 0 or more than 10000.
  • +
+
    +
  1. +

    Let's test that everything works as expected by adding a new test method and a few test cases:

    +
    [TestCase(int.MaxValue)]
    +[TestCase(-1)]
    +public void Recursive_WhenNIsOutOfBounds_ShouldThrowArgumentOutOfRange(
    +    int n
    +)
    +    => Assert.Throws<ArgumentOutOfRangeException>(() => FibonacciLib.Recursive(n));
    +
    +
  2. +
  3. +

    Rename the other method to Recursive_WhenNIsInBounds_ShouldReturnNthFibonacci

    +
  4. +
+
+
+

Unnecessary arguments

+
    +
  • We have one more problem:
  • +
  • Our Recursive function is a public method that has in total 4 arguments it takes, yet we only test 1 argument (n).
  • +
  • In order for our tests to be complete, we need to test all the arguments a public method takes in.
  • +
  • So, how do we fix this?
  • +
+
+
+

Removing unnecessary arguments

+
+
+
    +
  • The naive way would be to test all the arguments with more test cases.
  • +
  • It's important to realize that we don't have to.
  • +
  • Since we never pass in any other arguments than n from outside Recursive, we could just create another private function that has those arguments, and remove them from our public function.
  • +
+
+
+
public static long Recursive(int nStartingValue)
+{
+    if (nStartingValue < MinDepth
+    || nStartingValue > MaxDepth)
+        throw new ArgumentOutOfRangeException(
+          "n is out of bounds!"
+        );
+    
+    long getRecursive(
+        int n,
+        long previous = 0,
+        long current = 0,
+        int counter = 0)
+        => counter == n ?
+            current :
+            getRecursive(
+              n,
+              current,
+              Math.Max(previous + current, 1),
+              counter + 1
+            );
+    return getRecursive(nStartingValue);
+}
+
+
+
+
+
+

End Result

+
    +
  • Now we have tested all the arguments in our public Recursive function.
  • +
  • We do not need to test private functions such as getRecursive, only the public API has to be tested.
  • +
  • All tests should pass at this point.
  • +
+
+
+

Exercise 2: Unit Testing

+
public static long DoX(int nStartingValue) {
+    if (nStartingValue < 0 || nStartingValue > 150)
+        throw new ArgumentOutOfRangeException("n is out of bounds!");
+    
+    long doY(int n) => n > 0 ? n * doY(n - 1) : 1;
+    
+    return doY(nStartingValue);
+}
+
+
    +
  • Following the previously used architecture as closely as possible, unit test the attached function using NUnit. Answer the following questions:
  • +
+
    +
  1. What does the function that we are testing actually do?
  2. +
  3. Do we have to test doY as well?
  4. +
  5. How would you name DoX so it is descriptive? How about doY?
  6. +
+
+
+

Answers to exercise 2: Unit Testing

+ +
    +
  1. It gets the nth factorial (n!)
  2. +
  3. doY is a private method that gets tested when we test DoX, so no.
  4. +
  5. Example names: DoX TryGetFactorial, doY getFactorial
  6. +
+
+
+

Unit Testing: Final testing code

+
    +
  • +
    [TestCase(0, 1)]
    +[TestCase(1, 1)]
    +[TestCase(2, 2)]
    +[TestCase(3, 6)]
    +[TestCase(15, 1307674368000)]
    +public void TryGetFactorial_WhenNIsInBounds_ShouldReturnNthFactorial(
    +    int n, long expected
    +)
    +    => Assert.That(expected, Is.EqualTo(TestExerciseLib.TryGetFactorial(n)));
    +
    +
    [TestCase(-1)]
    +[TestCase(151)]
    +public void TryGetFactorial_NIsOutOfBounds_ShouldThrowArgumentOutOfRange(
    +    int n
    +)
    +    => Assert.Throws<ArgumentOutOfRangeException>(
    +        () => TestExerciseLib.TryGetFactorial(n)
    +    );
    +
    +
  • +
+
+
+

Writing testable code

+
    +
  • Writing testable code often means that you're writing good code
  • +
  • Testable units should +
      +
    • be deterministic (functional): same input always produces the same output
    • +
    • be loosely coupled: components have a loose and limited connection to other components in the system, meaning changes in one component should not require changes to many others
    • +
    • abide to the Single Responsibility Principle: a function, method or component is only responsible for one purpose
    • +
    +
  • +
+
+
+

Writing testable code: Example 1

+
+
+
    +
  • Can GetCurrentSeason() be unit tested? Why / why not?
  • +
  • How to fix the problems, if any?
  • +
+
+
+
public static class Season
+{
+    public static string GetCurrentSeason() {
+        DateTime currentTime = DateTime.Now;
+        if (currentTime.Month > 11 && currentTime.Month < 3)
+            return "Winter";
+        else if (currentTime.Month < 6)
+            return "Spring";
+        else if (currentTime.Month < 9)
+            return "Summer";
+        else
+            return "Fall";
+    }
+}
+
+
+
+
+
+

Writing testable code: Example 1 fixed

+
+
+
    +
  • Now the method is purely functional and deterministic +
      +
    • Will return the same value for every call with equal arguments
    • +
    +
  • +
  • GetCurrentSeason() can now easily be tested as shown next
  • +
+
+
+
public static class Season {
+    public static string GetCurrentSeason(
+        DateTime currentTime
+    ) {
+        if (currentTime.Month > 11 && currentTime.Month < 3)
+            return "Winter";
+        else if (currentTime.Month < 6)
+            return "Spring";
+        else if (currentTime.Month < 9)
+            return "Summer";
+        else
+            return "Fall";
+    }
+}
+
+
+
+
+
+
public class GetCurrentSeasonTest
+{
+    [TestCase(12, "Winter")]
+    [TestCase(3, "Spring")]
+    [TestCase(6, "Summer")]
+    [TestCase(9, "Fall")]
+    public void GetCurrentSeason_ForFirstMonthsOfSeasons_ReturnsCorrectSeason(
+        int month,
+        string season
+    )
+    {
+        Assert.That(season,
+          Is.EqualTo(
+            Season.GetCurrentSeason(new DateTime(2020, month, 1))
+          )
+        );
+    }
+}
+
+
+
+
+

+
+
    +
  • The tests fail, as shown above.
  • +
+
+
+
+
+
    +
  • Test results indicate there is something wrong with the code path that should return Winter
    if (currentTime.Month > 11 && currentTime.Month < 3)
    +
    +
  • +
  • The line should be changed to
    if (currentTime.Month > 11 || currentTime.Month < 3)
    +
    +
  • +
+
+
+

+
+
+
+
+

Writing testable code: Example 2

+
    +
  • Let's look at a HeatingUnit class that returns a heating setting based on the current season. The current date problem is back again:
    public class HeatingUnit
    +{
    +    public string GetHeatingSetting()
    +    {
    +        DateTime currentDate = DateTime.Now;
    +        if (Season.GetCurrentSeason(currentDate) == "Winter")
    +            return "HEAT_SETTING_HIGH";
    +        else if (Season.GetCurrentSeason(currentDate) == "Summer")
    +            return "HEAT_SETTING_OFF";
    +        else
    +            return "HEAT_SETTING_MEDIUM";
    +    }
    +}
    +
    +
  • +
+
+
+

Writing testable code: Example 2 continued

+
    +
  • Instead of taking the current date initialization even higher in the class hierarchy, let's make a service that returns the current date
    public interface IDateTimeProvider
    +{
    +    DateTime GetDateTime();
    +}
    +
    +
  • +
  • After this the service can be injected into HeatingUnit with constructor injection
  • +
  • Code example shown next
  • +
+
+
+
public class HeatingUnit
+{
+    private readonly IDateTimeProvider _dateTimeProvider;
+    
+    public HeatingUnit(IDateTimeProvider dateTimeProvider)
+    {
+        _dateTimeProvider = dateTimeProvider;
+    }
+    
+    public string GetHeatingSetting() {
+        if (Season.GetCurrentSeason(_dateTimeProvider.GetDateTime()) == "Winter")
+            return "HEAT_SETTING_HIGH";
+        else if (Season.GetCurrentSeason(_dateTimeProvider.GetDateTime()) == "Summer")
+            return "HEAT_SETTING_OFF";
+        else
+            return "HEAT_SETTING_MEDIUM";
+    }
+}
+
+
+
+
    +
  • +

    For testing, a fake DateTimeProvider service can be used for injecting the HeatingUnit with any date:

    +
    class FakeDateTimeProvider : IDateTimeProvider
    +{
    +    public DateTime Date { get; set; }
    +    public DateTime GetDateTime() => Date;
    +}
    +
    +
  • +
  • +

    Using this kind of a fake service instead of the real one is called mocking

    +
  • +
  • +

    In the real application the provider would return the real current date

    +
  • +
+
+
+
class SetHeatingSettingTest
+{
+    [TestCase(12, "HEAT_SETTING_HIGH")]
+    [TestCase(3, "HEAT_SETTING_MEDIUM")]
+    [TestCase(6, "HEAT_SETTING_OFF")]
+    [TestCase(9, "HEAT_SETTING_MEDIUM")]
+    public void SetHeatingSetting_ForFirstMonthsOfSeasons_ShouldReturnCorrectSetting(
+        int month,
+        string setting
+    )
+    {
+        FakeDateTimeProvider timeProvider
+            = new FakeDateTimeProvider { Date = new DateTime(2020, month, 1) };
+        HeatingUnit heatingUnit = new HeatingUnit(timeProvider);
+
+        Assert.That(setting, Is.EqualTo(heatingUnit.GetHeatingSetting()));
+    }
+}
+
+
+
+
+


+Tests should pass!

+
+
+
+

Testing with Postman

+
    +
  • So far, we have created individual request with Postman to see what the response of an API is for each request
  • +
  • This is not testing an API, this is exploring an API
  • +
  • Tests in Postman are inserted in the Tests tab +
      +
    • Postman uses the Chai.js testing library for creating tests
      +
    • +
    +
  • +
+
+
+
    +
  • To get started immediately, you can select snippets from the right
  • +
  • Let's select the Status code: Code is 200 snippet
    +
  • +
+
+
+
    +
  • Successfully run tests show up in green in the Test Result tab of the response:
    +
  • +
+
+
+
    +
  • The official Postman web page is a good starting point for learning testing with Postman
  • +
  • Using pm.expect will give a bit more info about the test:
    +
  • +
+
+
+
    +
  • The response to /serverinfo has the following body:
    {
    +    "appSettings": {
    +        "applicationUrl": "http://localhost:63741",
    +        "aspNetCoreEnvironment": "Development"
    +    },
    +    "serverStatus": {
    +        "dbConnectionStatus": "Connected"
    +    }
    +}
    +
    +
  • +
  • Let's make a test to see whether the server is connected to the database
  • +
+
+
+
    +
  • Variables can be declared within the test:
    +
  • +
+
+
+

Exercise 3: Testing with Postman

+ +
    +
  1. Create a new collection in Postman by selecting the Collections tab and clicking New Collection. Name it CourseAPI Tests. Launch the CourseAPI you have developed during the lectures, and make sure the course_db database is connected
  2. +
  3. Create a new GET request for the URI /api/courses. Add a test: status code of the request should be 200. Save the request to the CourseAPI Tests collection.
  4. +
  5. Create a new GET request for the URI /api/courses/999999999. Add a test: status code of the request should be 404. Save the request to the CourseAPI Tests collection.
  6. +
+
+
+
    +
  1. Create a new POST request for the URI /api/courses. Set the headers and the content correctly, but set the value of credits to 100. Add a test; status code should be 400. Save the request to the CourseAPI Tests collection.
  2. +
  3. Hover on CourseAPI Tests collection, click the arrow and click Run. From the opened window, scroll down and click Run CourseAPI Tests. This will create all the requests in your collection.
  4. +
+
+
+

CI/CD/CT

+
    +
  • CI stands for Continuous Integration +
      +
    • Each change in code triggers a build-and-test sequence for the given project
    • +
    • Goal is to have a consistent and automated way to build, package, and test applications
    • +
    +
  • +
  • CD stands for Continuous Delivery +
      +
    • Automates the delivery of applications to selected environments, such as Azure, AWS, GCP
    • +
    +
  • +
  • Both require Continuous Testing, which includes automated regression, performance and other tests
  • +
+
+
\ No newline at end of file diff --git a/8-testing.md b/8-testing.md index e78abd0..1de3357 100644 --- a/8-testing.md +++ b/8-testing.md @@ -1,817 +1,814 @@ +--- +marp: true +paginate: true +math: mathjax +theme: buutti +title: 8. Testing +--- + # Testing -![](imgs/9-testing_0.png) + + ---- +## Contents + +- [Introduction to testing](#introduction-to-testing) +- [C# Testing Frameworks](#c-testing-frameworks) +- [An extensive unit test example](#an-extensive-unit-test-example) +- [Writing testable code](#writing-testable-code) +- [Testing with Postman](#testing-with-postman) +- [CI/CD/CT](#cicdct) + +## Introduction to testing + +### What is testing? + +* Testing is much more than just trying out manually if the program works! +* [Manual testing](https://en.wikipedia.org/wiki/Manual_testing): acting as an end user of the application +* [Automated testing](https://en.wikipedia.org/wiki/Test_automation): using software to emulate the end user, or testing the functionality of the code itself +* Proving that the program is working as intended during development on a... + * ...***micro level***: individual pieces of code, such as methods + * ...***macro level***: larger functional components, such as services +* *All possible paths in a piece of code should be tested* + * i.e., [test coverage](https://en.wikipedia.org/wiki/Code_coverage) should be close to 100% + +### Automated testing: Why? -* What is testing? - * Proving that the program is working as intended during development on a - * micro-level: individual pieces of code, such as methods - * macro-level: larger functional components, such as services - * Automated testing & manual testing - * All possible paths in a piece of code should be tested * Why do automated testing? - * Sets ground truths that can be relied on during development: "With this kind of input, this should happen" - * No need to take the programmers word for it - * Ensures code functionality before moving on, "finding bugs before debugging" + * Sets ground truths that can be relied on during development: + * *"With this kind of input, this should happen"* + * (No need to take the programmers word for it) + * Ensures code functionality before moving on + * Ideally finding bugs before starting to debug * Guides developers to create more testable and thus more readable, interchangeable and overall better code + * [Test-driven development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development) is based on writing tests first and code later! -# Automated Testing +### Types of automated testing * Unit testing * Testing individual units (methods, classes) within a project - * "input x produces output y" - * Each unit has to be entirely independent and deterministic for it to be unit testable! - * Deterministic = Always the same result with the same input -* Integration Testing + * *"input x produces output y"* + * Each unit has to be entirely independent and ***deterministic*** for it to be unit testable! + * Deterministic: Always the same result with the same input +* Integration testing * Testing complete modules and the interaction between them within a project * Closer to real life scenarios +* End-to-end (E2E) testing + * Testing the whole program by emulating the end user's behaviour + +### Test type comparison + +* Unit tests are faster to write, E2E tests slowest -# C# Testing Frameworks +
+ +![w:800px](https://microsoft.github.io/code-with-engineering-playbook/automated-testing/cdc-testing/images/testing-pyramid.png) + +
+ +## C# Testing Frameworks * Three main testing frameworks: - * MSTest - * NUnit - * XUnit + * `MSTest` + * `NUnit` + * `XUnit` * You can install these using NuGet, or in the newer versions of Visual Studio you can create a new testing project -* NUnit is the most popular of the three, __and will be used in this lecture__ +* NUnit is the most popular of the three, and will be used in this lecture -# MSTest +### MSTest * Built-in to most versions of Visual Studio since 2005 * Simple to use, easy structure - * TestClass for denoting classes containing unit tests - * TestMethod for denoting unit test methods + * `TestClass` for denoting classes containing unit tests + * `TestMethod` for denoting unit test methods -# NUnit +### NUnit * Most popular C# unit testing framework * Very similar structure to MSTest tests - * Instead of TestClass, we use __TestFixture__ - * Instead of TestMethod, we use __Test / TestCase__ -* Has some advantages to MSTest or xUnit tests, for example: + * Instead of `TestClass`, we use `TestFixture` + * Instead of `TestMethod`, we use `Test` / `TestCase` +* Has some advantages to MSTest or XUnit tests, for example: * Ability to test a single test method using multiple different arguments -# XUnit - -Free, open-source unit testing tool for .NET framework and .NET core - -Made by the creator of NUnit v2 +### XUnit -You can test C#, F#, VB.NET and other .NET languages +* Free, open-source unit testing tool for .NET framework and .NET core +* Made by the creator of NUnit v2 +* You can test C#, F#, VB.NET and other .NET languages +* Part of the .NET Foundation +* Unique and different style -Part of the .NET Foundation +### Note about test case naming -Unique and different style +* In C# unit testing, the most commonly followed naming convention for test cases/methods is as follows: + ```csharp + FunctionNameThatIsTested_Condition_ShouldDoX + ``` +* For example: + ```csharp + Fibonacci_WhenNIs0_ShouldReturn0 + ``` -# Test Case Naming Conventions +## An extensive unit test Example -In C# unit testing, the most commonly followed naming convention for test cases/methods is as follows: - -FunctionNameThatIsTested_Condition_ShouldDoX - -For example: - -Fibonacci_WhenNIs0_ShouldReturn0 - -# Solution & Sample Project +### Creating a solution & sample project First, we need a solution and a sample project. -In Visual Studio, create a class library that targets __.NET Standard__ . +
+
-Call the project "Fibonacci.Library", and solution "Fibonacci". Remember to uncheck " _Place solution and project in the same directory_ " if it isn't unchecked already. +1) In Visual Studio, create a ***Class library*** that targets *__.NET Standard__*. +2) Name the project `Fibonacci.Library` and the solution `Fibonacci`. Remember to uncheck _Place solution and project in the same directory_ if it isn't unchecked already. -# Solution & Sample Project (continued) +
+
![](imgs/9-testing_1.png) -Next, rename the default "Class1.cs" file to "Fibonacci.cs". - -Allow Visual Studio to also rename the class itself to Fibonacci. - -Make the class public and static. - -Now, let's __ __ add a recursive fibonacci function, as shown in the next slide: - -using System; - -namespace Fibonacci.Library { - -public static class Fibonacci { - -public static long Recursive( - -int n, - -long previous = 0, - -long current = 0, - -int counter = 0 - -) { - -return counter == n ? - -previous + current : - -Recursive(n, current, Math.Max(previous + current, 1), counter + 1); - -} - -} - -} - -# Adding a Testing Project - -Now we need to add a testing project to our solution. - -Right click your solution, and add a new project. Select the NUnit project for __.NET Core__ , and call it "Fibonacci.UnitTests". - -Rename the created class file to "Fibonacci_Tests.cs", and the class to "Fibonacci_Tests" - -# Adding a Testing Project (continued) - -We then need to link the Fibonacci.Library project to Fibonacci.UnitTests. - -Right click Fibonacci.UnitTests, and select Add > Reference…, and under "Projects" select Fibonacci.Library. Press OK. - -Now we have linked Fibonacci.Library to Fibonacci.UnitTests, and we can begin testing our Recursive-function in the Fibonacci-class. - -# Test Fixture For a Class - -Since we have many things called "Fibonacci", we have to import our Fibonacci-class by using an alias: - -using FibonacciLib = Fibonacci.Library.Fibonacci; - -Put this to the top of your Fibonacci_Tests.cs file, underneath using NUnit.Framework;. - -# Test Fixture For a Class (continued) - -Now, add [TestFixture] on top of our class, like so: - -using NUnit.Framework; - -using FibonacciLib = Fibonacci.Library.Fibonacci; - -namespace Fibonacci.UnitTests { - -[TestFixture] - -public class Fibonacci_Tests { +
+
-This denotes that this class contains our unit tests. +### Adding the function to be tested -Then, inside our class Fibonacci_Tests, let's create our first method to test the Recursive-function, as shown in the next slide: +
+
-using NUnit.Framework; +3) Rename the default `Class1.cs` file to `Fibonacci.cs`. +4) Allow Visual Studio to also rename the class itself to `Fibonacci`. + * Make the class `public` and `static`. +5) Add a *recursive fibonacci function* shown here. -using FibonacciLib = Fibonacci.Library.Fibonacci; +
+
-namespace Fibonacci.UnitTests { - -[TestFixture] - -public class Fibonacci_Tests { - -[Test] - -public void Recursive_WhenNIs0_ShouldReturn0() - -=> Assert.That(0, Is.EqualTo(FibonacciLib.Recursive(n: 0))); - -} +```csharp +using System; +namespace Fibonacci.Library +{ + public static class Fibonacci + { + public static long Recursive( + int n, + long previous = 0, + long current = 0, + int counter = 0 + ) + { + return counter == n ? + previous + current : + Recursive( + n, + current, + Math.Max(previous + current, 1), + counter + 1 + ); + } + } } +``` -* Things to take into consideration: -* When passing in function arguments, to avoid magic numbers and to improve code readability, use the syntax _argumentName: value_ - * Magic numbers = using numbers as input as-is without explanation, for example without assigning to a well-named variable -* Assert.That is a function (but not the only function) that determines whether the test passes. - * Its first argument is the expected value. In the second argument you give a constraint for the actual output, i.e. value is equal to, greater than, starts with, and [many others](https://docs.nunit.org/articles/nunit/writing-tests/constraints/Constraints.html) . - * Do not mix the arguments up! If you do, the errors you get do not make any sense. +
+
-# Running Tests -Now, let's open the built-in test runner in Visual Studio to run our tests. +### Adding a testing project -On the top of Visual Studio, click "Test", then "Test Explorer". +* Now we need to add a testing project to our solution. +6) Right click your solution, and add a new project. Select the NUnit project for **_.NET Core_**, and call it `Fibonacci.UnitTests`. +7) Rename the created class file to `Fibonacci_Tests.cs`, and the class to `Fibonacci_Tests` -In the test explorer, you should see the test case we created. If not, build the solution, and make sure there are no errors. +### Linking the testing project -Either way, let's run our test case. Press "Run All Tests" in the top-left corner of test explorer. You should see our test case __passing__ . +8) We then need to link the `Fibonacci.Library` project to `Fibonacci.UnitTests`. +9) Right click `Fibonacci.UnitTests`, and select *Add > Reference...*, and under *Projects* select `Fibonacci.Library`. Press OK. -# Adding More Test Cases +* Now we have linked `Fibonacci.Library` to `Fibonacci.UnitTests`, and we can begin testing our `Recursive` function in the `Fibonacci` class. -While there's nothing inherently wrong about our test case, it doesn't cover much of anything. We need to add more test cases to test different values and outputs. +### Importing the class -In other testing frameworks, you would do this by creating more methods. +10) Since we have many things called "Fibonacci", we have to import our `Fibonacci` class by using an alias + * Put this to the top of your file `Fibonacci_Tests.cs`, underneath
`using NUnit.Framework;` + ```csharp + using FibonacciLib = Fibonacci.Library.Fibonacci; + ``` -But with NUnit, we can use our existing method. +### Test fixture for a class -# Adding More Test Cases (continued) +11) Now, add `[TestFixture]` on top of our class, like so: + ```csharp + using NUnit.Framework; + using FibonacciLib = Fibonacci.Library.Fibonacci; -Do the following modifications to our test method: + namespace Fibonacci.UnitTests { + [TestFixture] + public class Fibonacci_Tests { + ``` -[TestCase(0, 0)] +* This denotes that this class contains our unit tests. -public void Recursive_Always_ShouldReturnNthFibonacci( +### Adding the unit test method -int n, +12) Then, inside our class `Fibonacci_Tests`, let's create our first method to test the `Recursive` function, as shown here: + ```csharp + using NUnit.Framework; + using FibonacciLib = Fibonacci.Library.Fibonacci; -long expected + namespace Fibonacci.UnitTests { + [TestFixture] + public class Fibonacci_Tests { + [Test] + public void Recursive_WhenNIs0_ShouldReturn0() + => Assert.That(0, Is.EqualTo(FibonacciLib.Recursive(n: 0))); + } + } + ``` -) +### Things to take into consideration -=> Assert.That(expected, Is.EqualTo(FibonacciLib.Recursive(n))); +* When passing in function arguments, to avoid ***magic numbers*** and to improve code readability, use the syntax `argumentName: value` + * Magic numbers = using numbers as input as-is without explanation, for example without assigning to a well-named variable, or without using a descriptive type +* `Assert.That` is one of many functions that can determine whether the test passes + * Its first argument is the ***expected value***. In the second argument you give a ***constraint*** for the actual output, i.e., value is equal to, greater than, starts with, [etc](https://docs.nunit.org/articles/nunit/writing-tests/constraints/Constraints.html). + * *Do not mix the arguments up!* If you do, the errors you get do not make any sense. -Using TestCase instead of Test, we can pass in arguments to our test case method, and we can use those passed in arguments in our actual tests. +### Running Tests -Run the test cases again, and they should __pass__ . +* Now, let's open the built-in test runner in Visual Studio to run our tests. -But as you might notice, we haven't really changed anything yet. +* On the top of Visual Studio, click *Test*, then *Test Explorer*. +* In the Test Explorer, you should see the test case we created. If not, build the solution, and make sure there are no errors. +* Either way, let's run our test case. Press *Run All Tests* in the top-left corner of Test Explorer. You should see our test case *__passing__*. -Let's add more test cases, like so: +### Adding More Test Cases -[TestCase(0, 0)] +* While there's nothing inherently wrong about our test case, it doesn't cover much of anything. +* We need to add more test cases to test different values and outputs. +* In other testing frameworks, you would do this by creating more methods. +* But with `NUnit`, we can use our *existing* method. -[TestCase(1, 1)] +### Adding a new test -public void Recursive_Always_ShouldReturnNthFibonacci( +1) Do the following modifications to our test method: + ```csharp + [TestCase(0, 0)] + public void Recursive_Always_ShouldReturnNthFibonacci( + int n, + long expected + ) + => Assert.That(expected, Is.EqualTo(FibonacciLib.Recursive(n))); + ``` + * Using `TestCase` instead of `Test`, we can pass in arguments to our test case method, and we can use those passed in arguments in our actual tests. -Now we have 2 test cases with only one test method. Run the tests again, and both cases should __pass__ . +### Adding more tests -Let's add one more TestCase, like so: +2) Run the test cases again, and they should *__pass__*. + * But as you might notice, we haven't really changed anything yet. +3) Let's add more test cases, like so: + ```csharp + [TestCase(0, 0)] + [TestCase(1, 1)] + public void Recursive_Always_ShouldReturnNthFibonacci( + ``` -[TestCase(2, 1)] +4) Now we have 2 test cases with only one test method. Run the tests again, and both cases should *__pass__*. -Run the tests again, and you should see the test case __fail__ . +### Adding a failing test -This is to be expected: the 2nd fibonacci number should be 1, but the actual value we got from our function was 2. +5) Let's add one more TestCase, like so: + ```csharp + [TestCase(2, 1)] + ``` +6) Run the tests again, and you should see the test case *__fail__*. + * This is to be expected: the 2nd fibonacci number should be 1, but the actual value we got from our function was 2. + * This means our function does not work as expected. -This means our function does not work as expected. +### Fixing our code -Let's fix it, as shown in the next slide: +
+
+ +7) Let's fix our function, as seen here. +8) Run the tests again. You should see all *__pass__* now. +9) Add a few more test cases + * (If you don't know the fibonacci sequence, you can check it from the internet.) +
+
+ +```csharp public static long Recursive( - -int n, - -long previous = 0, - -long current = 0, - -int counter = 0 - -) { - -return counter == n ? - -current : // changed from previous + current - -Recursive(n, current, Math.Max(previous + current, 1), counter + 1); - + int n, + long previous = 0, + long current = 0, + int counter = 0 +) +{ + return counter == n ? + current : // changed from previous + current + Recursive( + n, + current, + Math.Max(previous + current, 1), + counter + 1 + ); } +``` -Run the tests again. You should see all __pass __ now. - -Add a few more test cases; if you don't know the fibonacci sequence, you can check it from google. - -# Exercise 1: Unit testing in practice - -* Answer the following questions: -* How many test cases should we ideally have to test our Recursive-function? -* What happens if we pass in n = int.MaxValue (2147483647)? Or other huge numbers? - * How would you fix the function so it works as expected? -* What happens if we pass in n = -1 ? Or other negative numbers? - * How would you fix the function so it works as expected? -* (Answers on the next slide) - -# Unit testing in practice: Exercise - Answers - -* Answers: -* We should cover all the edge cases and code paths with our test cases. We do not need to have many test cases for the same code path. -* Stack Overflow, Int64 Overflow, or the test case just won't run. - * We should have a maximum allowed value for n, throw argument out of range exception if above -* Stack Overflow, since current never can equal n that is less than 0 - * We should have a minimum allowed value for n, throw argument out of range exception if below - -# Testing Exceptions - -To expect exceptions to be thrown in test cases, we can use the method - -Assert.Throws, syntax is as follows: - -Assert.Throws(delegate); - -In practice: +
+
-Assert.Throws(() => TestedFunction(n)); +### Exercise 1: Unit testing in practice + -# Handling Border Cases - -We need to add handling for n that is less than 0 or more than a set amount. Then we need to add another test method and two test cases to test our new functionality. - -Let's start with modifying our existing Recursive-function, as shown in the next slide: - -# Handling Border Cases (continued) +Answer the following questions: +1) How many test cases should we ideally have to test our `Recursive` function? +2) What happens if we pass in `n = int.MaxValue (2147483647)`? + * What about other huge numbers? + * How would you fix the function so it works as expected? +3) What happens if we pass in `n = -1`? + * What about other negative numbers? + * How would you fix the function so it works as expected? + +### Answers to Exercise 1: Unit testing in practice + + +1) We should cover all the edge cases and code paths with our test cases. We do not need to have many test cases for the same code path. +2) `Stack Overflow`, `Int64 Overflow`, or the test case just won't run. + * We should have a maximum allowed value for `n`, throw the ***argument out of range*** exception if above +3) `Stack Overflow`, since `current` can never equal `n` that is less than `0` + * We should have a minimum allowed value for `n`, throw the ***argument out of range*** exception if below + +### Testing exceptions + +* To expect exceptions to be thrown in test cases, we can use the method `Assert.Throws` +* Syntax is as follows: + ```csharp + Assert.Throws(delegate); + ``` +* In practice: + ```csharp + Assert.Throws(() => TestedFunction(n)); + ``` +### Handling border cases + +
+
+ +* Continuing our Fibonacci test case, we need to add handling for `n` that is less than `0` or more than a set amount. Then we need to add another test method and two test cases to test our new functionality. +1) Let's start with modifying our existing `Recursive` function, as shown here: + +
+
+ +```csharp public const int MinDepth = 0; - public const int MaxDepth = 10000; public static long Recursive( - -int n, - -long previous = 0, - -long current = 0, - -int counter = 0 - -) { - -if (n < MinDepth || n > MaxDepth) - -throw new ArgumentOutOfRangeException("n is out of bounds!"); - -return counter == n ? - -current : - -Recursive(n, current, Math.Max(previous + current, 1), counter + 1); - -} - -Now our Recursive-function throws an ArgumentOutOfRange exception when n is less than 0 or more than 10000. Let's test that everything works as expected, by adding a new test method and a few test cases: - -[TestCase(int.MaxValue)] - -[TestCase(-1)] - -public void Recursive_WhenNIsOutOfBounds_ShouldThrowArgumentOutOfRange( - -int n - + int n, + long previous = 0, + long current = 0, + int counter = 0 ) +{ + if (n < MinDepth || n > MaxDepth) + throw new ArgumentOutOfRangeException( + "n is out of bounds!" + ); + + return counter == n ? + current : + Recursive( + n, + current, + Math.Max(previous + current, 1), + counter + 1 + ); +} +``` -=> Assert.Throws(() => FibonacciLib.Recursive(n)); - -__Rename the other method to Recursive_WhenNIsInBounds_ShouldReturnNthFibonacci__ - -# Removing Unnecessary Arguments - -We have one more problem: - -Our Recursive-function is a public method that has in total 4 arguments it takes, yet we only test 1 argument (n). - -In order for our tests to be complete, we need to test all the arguments a public method takes in. - -So, how do we fix this? - -# Removing Unnecessary Arguments (continued) - -The naive way would be to simply test all the arguments with more test cases. - -But it's important to realize that __we don't have to__ . Since we basically never pass in any other arguments than n from outside Recursive, we could just create another __private __ function that has those arguments, and we could remove them from our __public __ Recursive-function. - -Like shown in the next slide: - -public static long Recursive(int nStartingValue) { - -if (nStartingValue < MinDepth || nStartingValue> MaxDepth) - -throw new ArgumentOutOfRangeException("n is out of bounds!"); +
+
-long getRecursive( +### Testing that everything works -int n, +* Now our `Recursive` function throws an `ArgumentOutOfRange` exception when `n` is less than `0` or more than `10000`. +2) Let's test that everything works as expected by adding a new test method and a few test cases: + ```csharp + [TestCase(int.MaxValue)] + [TestCase(-1)] + public void Recursive_WhenNIsOutOfBounds_ShouldThrowArgumentOutOfRange( + int n + ) + => Assert.Throws(() => FibonacciLib.Recursive(n)); + ``` -long previous = 0, +3) Rename the other method to `Recursive_WhenNIsInBounds_ShouldReturnNthFibonacci` -long current = 0, +### Unnecessary arguments -int counter = 0) +* We have one more problem: +* Our `Recursive` function is a `public` method that has in total 4 arguments it takes, yet we only test 1 argument (`n`). +* In order for our tests to be complete, we need to test all the arguments a `public` method takes in. +* So, how do we fix this? -=> counter == n ? +### Removing unnecessary arguments -current : +
+
-getRecursive(n, current, Math.Max(previous + current, 1), counter + 1); +* The naive way would be to test all the arguments with more test cases. +* It's important to realize that *__we don't have to__*. +* Since we never pass in any other arguments than `n` from outside `Recursive`, we could just create another `private` function that has those arguments, and remove them from our public function. -return getRecursive(nStartingValue); +
+
+```csharp +public static long Recursive(int nStartingValue) +{ + if (nStartingValue < MinDepth + || nStartingValue > MaxDepth) + throw new ArgumentOutOfRangeException( + "n is out of bounds!" + ); + + long getRecursive( + int n, + long previous = 0, + long current = 0, + int counter = 0) + => counter == n ? + current : + getRecursive( + n, + current, + Math.Max(previous + current, 1), + counter + 1 + ); + return getRecursive(nStartingValue); } +``` -# End Result - -Now we have tested all the arguments in our public Recursive function. +
+
-We do not need to test private functions such as getRecursive, only the public API has to be tested. +### End Result -All tests should __pass __ by this point. +* Now we have tested all the arguments in our public `Recursive` function. +* We do not need to test private functions such as `getRecursive`, only the public API has to be tested. +* All tests should *__pass__* at this point. -# Exercise 2: Unit Testing - -Following the previously used architecture as closely as possible, unit test the following function using NUnit: - -Questions on the next slide. +### Exercise 2: Unit Testing + +```csharp public static long DoX(int nStartingValue) { - -if (nStartingValue < 0 || nStartingValue > 150) - -throw new ArgumentOutOfRangeException("n is out of bounds!"); - -long doY(int n) => n > 0 ? n * doY(n - 1) : 1; - -return doY(nStartingValue); - + if (nStartingValue < 0 || nStartingValue > 150) + throw new ArgumentOutOfRangeException("n is out of bounds!"); + + long doY(int n) => n > 0 ? n * doY(n - 1) : 1; + + return doY(nStartingValue); } - -# Unit Testing: Exercise - Questions - -Answer the following questions: - -What does the function that we are testing actually do? - -Do we have to test doY as well? - -How would you name DoX so it is descriptive? How about doY? - -# Unit Testing: Exercise - Answers - -* It gets the nth factorial (n!) -* doY is a private method that gets tested when we test DoX, so no. -* DoX = TryGetFactorial, doY = getFactorial - * __Name your functions to these__ - -# Unit Testing - Final Testing Code Part 1 - -[TestCase(0, 1)] - -[TestCase(1, 1)] - -[TestCase(2, 2)] - -[TestCase(3, 6)] - -[TestCase(15, 1307674368000)] - -public void TryGetFactorial_WhenNIsInBounds_ShouldReturnNthFactorial( - -int n, long expected - -) - -=> Assert.That(expected, Is.EqualTo(TestExerciseLib.TryGetFactorial(n))); - -[TestCase(-1)] - -[TestCase(151)] - -public void TryGetFactorial_NIsOutOfBounds_ShouldThrowArgumentOutOfRange( - -int n - -) - -=> Assert.Throws( - -() => TestExerciseLib.TryGetFactorial(n) - -); - -# Writing Testable Code - -* Writing testable code equals writing good code +``` + +* Following the previously used architecture as closely as possible, unit test the attached function using `NUnit`. Answer the following questions: +1) What does the function that we are testing actually *do*? +2) Do we have to test `doY` as well? +3) How would you name `DoX` so it is descriptive? How about `doY`? + +### Answers to exercise 2: Unit Testing + + +1) It gets the nth factorial (`n!`) +2) `doY` is a private method that gets tested when we test `DoX`, so no. +3) Example names: `DoX` $\Rightarrow$ `TryGetFactorial`, `doY` $\Rightarrow$ `getFactorial` + + +### Unit Testing: Final testing code + +* + ```csharp + [TestCase(0, 1)] + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(3, 6)] + [TestCase(15, 1307674368000)] + public void TryGetFactorial_WhenNIsInBounds_ShouldReturnNthFactorial( + int n, long expected + ) + => Assert.That(expected, Is.EqualTo(TestExerciseLib.TryGetFactorial(n))); + ``` + + ```csharp + [TestCase(-1)] + [TestCase(151)] + public void TryGetFactorial_NIsOutOfBounds_ShouldThrowArgumentOutOfRange( + int n + ) + => Assert.Throws( + () => TestExerciseLib.TryGetFactorial(n) + ); + ``` + +## Writing testable code + +* Writing testable code often means that you're writing good code * Testable units should * be deterministic (functional): same input always produces the same output - * be loosely coupled: components have a loose and limited connection to other components in the system, meaning changes in one component should not require changes to many others - * abide to the _Single Responsibility Principle: _ a function, method or component is only responsible for one purpose ---- - -https://en.wikipedia.org/wiki/Loose_coupling - -# Writing Testable Code - Example 1 - -public static class Season - -{ - -public static string GetCurrentSeason() - -{ - -DateTime currentTime = DateTime.Now; - -if (currentTime.Month > 11 && currentTime.Month < 3) - -return "Winter"; - -else if (currentTime.Month < 6) + * be [loosely coupled](https://en.wikipedia.org/wiki/Loose_coupling): components have a loose and limited connection to other components in the system, meaning changes in one component should not require changes to many others + * abide to the *__Single Responsibility Principle__*: a function, method or component is only responsible for one purpose -return "Spring"; +### Writing testable code: Example 1 -else if (currentTime.Month < 9) +
+
-return "Summer"; +* Can `GetCurrentSeason()` be unit tested? Why / why not? +* How to fix the problems, if any? -else - -return "Fall"; - -} - -} - -Can GetCurrentSeason() be unit tested? - -Why / why not? - -How to fix the problems, if any? - -# Writing Testable Code - Example 1 (continued) +
+
+```csharp public static class Season - { + public static string GetCurrentSeason() { + DateTime currentTime = DateTime.Now; + if (currentTime.Month > 11 && currentTime.Month < 3) + return "Winter"; + else if (currentTime.Month < 6) + return "Spring"; + else if (currentTime.Month < 9) + return "Summer"; + else + return "Fall"; + } +} +``` -public static string GetCurrentSeason( - -DateTime currentTime - -) - -{ - -if (currentTime.Month > 11 && currentTime.Month < 3) - -return "Winter"; - -else if (currentTime.Month < 6) - -return "Spring"; - -else if (currentTime.Month < 9) - -return "Summer"; - -else - -return "Fall"; +
+
-} +### Writing testable code: Example 1 fixed -} +
+
* Now the method is purely functional and deterministic * Will return the same value for every call with equal arguments -* GetCurrentSeason() can now easily be tested as shown in the next slide - -public class GetCurrentSeasonTest - -{ - -[TestCase(12, "Winter")] - -[TestCase(3, "Spring")] - -[TestCase(6, "Summer")] - -[TestCase(9, "Fall")] - -public void GetCurrentSeason_ForFirstMonthsOfSeasons_ReturnsCorrectSeason( - -int month, - -string season - -) - -{ - -Assert.That(season, Is.EqualTo(Season.GetCurrentSeason(new DateTime(2020, month, 1)))); - +* `GetCurrentSeason()` can now easily be tested as shown next + +
+
+ +```csharp +public static class Season { + public static string GetCurrentSeason( + DateTime currentTime + ) { + if (currentTime.Month > 11 && currentTime.Month < 3) + return "Winter"; + else if (currentTime.Month < 6) + return "Spring"; + else if (currentTime.Month < 9) + return "Summer"; + else + return "Fall"; + } } +``` -} - -![](imgs/9-testing_2.png) - -Test results indicate there is something wrong with the code path that should return "Winter" - -if (currentTime.Month > 11 && currentTime.Month < 3) - -The line is changed to - -if (currentTime.Month > 11 || currentTime.Month < 3) - -![](imgs/9-testing_3.png) - -# Writing Testable Code - Example 2 - -Let's look at a HeatingUnit class, which returns a heating setting based on the current season. The current date problem is back again: - -public class HeatingUnit +
+
-{ - -public string GetHeatingSetting() +--- +```csharp +public class GetCurrentSeasonTest { - -DateTime currentDate = DateTime.Now; - -if (Season.GetCurrentSeason(currentDate) == "Winter") - -return "HEAT_SETTING_HIGH"; - -else if (Season.GetCurrentSeason(currentDate) == "Summer") - -return "HEAT_SETTING_OFF"; - -else - -return "HEAT_SETTING_MEDIUM"; - -} - + [TestCase(12, "Winter")] + [TestCase(3, "Spring")] + [TestCase(6, "Summer")] + [TestCase(9, "Fall")] + public void GetCurrentSeason_ForFirstMonthsOfSeasons_ReturnsCorrectSeason( + int month, + string season + ) + { + Assert.That(season, + Is.EqualTo( + Season.GetCurrentSeason(new DateTime(2020, month, 1)) + ) + ); + } } +``` -# Writing Testable Code - Example 2 (continued) - -Instead of taking the current date initialization even higher in the class hierarchy, let's make a service that returns the current date - -public interface IDateTimeProvider +--- -{ +
-DateTime GetDateTime(); +![](imgs/9-testing_2.png) -} +
-After this the service can be injected into HeatingUnit with constructor injection (next slide) +* The tests fail, as shown above. -public class HeatingUnit +--- -{ +
+
-private readonly IDateTimeProvider _dateTimeProvider; +* Test results indicate there is something wrong with the code path that should return `Winter` + ```csharp + if (currentTime.Month > 11 && currentTime.Month < 3) + ``` +* The line should be changed to + ```csharp + if (currentTime.Month > 11 || currentTime.Month < 3) + ``` -public HeatingUnit(IDateTimeProvider dateTimeProvider) +
+
-{ +![](imgs/9-testing_3.png) -_dateTimeProvider = dateTimeProvider; +
+
+ +### Writing testable code: Example 2 + +* Let's look at a `HeatingUnit` class that returns a heating setting based on the current season. The current date problem is back again: + ```csharp + public class HeatingUnit + { + public string GetHeatingSetting() + { + DateTime currentDate = DateTime.Now; + if (Season.GetCurrentSeason(currentDate) == "Winter") + return "HEAT_SETTING_HIGH"; + else if (Season.GetCurrentSeason(currentDate) == "Summer") + return "HEAT_SETTING_OFF"; + else + return "HEAT_SETTING_MEDIUM"; + } + } + ``` + +### Writing testable code: Example 2 continued + +* Instead of taking the current date initialization even higher in the class hierarchy, let's make a ***service*** that returns the current date + ```csharp + public interface IDateTimeProvider + { + DateTime GetDateTime(); + } + ``` +* After this the service can be injected into `HeatingUnit` with constructor injection +* Code example shown next -} - -public string GetHeatingSetting() +--- +```csharp +public class HeatingUnit { - -if (Season.GetCurrentSeason(_dateTimeProvider.GetDateTime()) == "Winter") - -return "HEAT_SETTING_HIGH"; - -else if (Season.GetCurrentSeason(_dateTimeProvider.GetDateTime()) == "Summer") - -return "HEAT_SETTING_OFF"; - -else - -return "HEAT_SETTING_MEDIUM"; - + private readonly IDateTimeProvider _dateTimeProvider; + + public HeatingUnit(IDateTimeProvider dateTimeProvider) + { + _dateTimeProvider = dateTimeProvider; + } + + public string GetHeatingSetting() { + if (Season.GetCurrentSeason(_dateTimeProvider.GetDateTime()) == "Winter") + return "HEAT_SETTING_HIGH"; + else if (Season.GetCurrentSeason(_dateTimeProvider.GetDateTime()) == "Summer") + return "HEAT_SETTING_OFF"; + else + return "HEAT_SETTING_MEDIUM"; + } } +``` -} - -For testing, a fake DateTimeProvider service can be used for injecting the HeatingUnit with any date: - -class FakeDateTimeProvider : IDateTimeProvider - -{ +--- -public DateTime Date { get; set; } +* For testing, a fake `DateTimeProvider` service can be used for injecting the `HeatingUnit` with any date: -public DateTime GetDateTime() => Date; + ```csharp + class FakeDateTimeProvider : IDateTimeProvider + { + public DateTime Date { get; set; } + public DateTime GetDateTime() => Date; + } + ``` -} +* Using this kind of a fake service instead of the real one is called [mocking](https://en.wikipedia.org/wiki/Mock_object) +* In the real application the provider would return the real current date -In the real application the provider would return the real current date +--- +```csharp class SetHeatingSettingTest - { - -[TestCase(12, "HEAT_SETTING_HIGH")] - -[TestCase(3, "HEAT_SETTING_MEDIUM")] - -[TestCase(6, "HEAT_SETTING_OFF")] - -[TestCase(9, "HEAT_SETTING_MEDIUM")] - -public void SetHeatingSetting_ForFirstMonthsOfSeasons_ShouldReturnCorrectSetting( - -int month, - -string setting - -) - -{ - -FakeDateTimeProvider timeProvider - -= new FakeDateTimeProvider { Date = new DateTime(2020, month, 1) }; - -HeatingUnit heatingUnit = new HeatingUnit(timeProvider); - -Assert.That(setting, Is.EqualTo(heatingUnit.GetHeatingSetting())); - + [TestCase(12, "HEAT_SETTING_HIGH")] + [TestCase(3, "HEAT_SETTING_MEDIUM")] + [TestCase(6, "HEAT_SETTING_OFF")] + [TestCase(9, "HEAT_SETTING_MEDIUM")] + public void SetHeatingSetting_ForFirstMonthsOfSeasons_ShouldReturnCorrectSetting( + int month, + string setting + ) + { + FakeDateTimeProvider timeProvider + = new FakeDateTimeProvider { Date = new DateTime(2020, month, 1) }; + HeatingUnit heatingUnit = new HeatingUnit(timeProvider); + + Assert.That(setting, Is.EqualTo(heatingUnit.GetHeatingSetting())); + } } - -} - -![](imgs/9-testing_4.png) +``` --- -Näytä demo - -# Testing with Postman +
-* So far, you have created individual request with Postman to see what the response of an API is for each request -* This is __not testing __ an API, this is __exploring __ an API -* Tests in Postman are inserted in the _Tests _ tab - * Postman uses __Chai.js__ testing library for creating tests - -![](imgs/9-testing_5.png) - -To get started immediately, you can select snippets from the right - -Let's select the "Status code: Code is 200" snippet - -![](imgs/9-testing_6.png) - -Successfully ran tests show up in green in the Test Result tab of the response: - -![](imgs/9-testing_7.png) - -[The official Postman web page](https://learning.postman.com/docs/writing-scripts/test-scripts/) is a good starting point for learning testing with Postman - -Using pm.expect will give a bit more info about the test: - -![](imgs/9-testing_8.png) - -The response to /serverinfo has the following body: +![](imgs/9-testing_4.png) +Tests should pass! -{ +
-"appSettings": { +## Testing with Postman -"applicationUrl": "http://localhost:63741", +* So far, we have created individual request with Postman to see what the response of an API is for each request +* This is not testing an API, this is *__exploring__* an API +* Tests in Postman are inserted in the _Tests_ tab + * Postman uses the *__Chai.js__* testing library for creating tests + ![](imgs/9-testing_5.png) -"aspNetCoreEnvironment": "Development" +--- -}, +* To get started immediately, you can select *snippets* from the right +* Let's select the *Status code: Code is 200* snippet + ![](imgs/9-testing_6.png) -"serverStatus": { +--- -"dbConnectionStatus": "Connected" +* Successfully run tests show up in green in the *Test Result* tab of the response: + ![](imgs/9-testing_7.png) -} +--- -} +* [The official Postman web page](https://learning.postman.com/docs/writing-scripts/test-scripts/) is a good starting point for learning testing with Postman +* Using `pm.expect` will give a bit more info about the test: + ![](imgs/9-testing_8.png) -Let's make a test to see whether the server is connected to the database +--- -Variables can be declared within the test: +* The response to /serverinfo has the following body: + ```json + { + "appSettings": { + "applicationUrl": "http://localhost:63741", + "aspNetCoreEnvironment": "Development" + }, + "serverStatus": { + "dbConnectionStatus": "Connected" + } + } + ``` +* Let's make a test to see whether the server is connected to the database -![](imgs/9-testing_9.png) +--- -# Exercise 3: Testing with Postman +* Variables can be declared within the test: + ![](imgs/9-testing_9.png) -Create a new collection in Postman by selecting the _Collections _ tab and clicking _New Collection._ Name it _CourseAPI Tests. _ Launch the CourseAPI you have developed during the lectures, and make sure the course_db database is connected +### Exercise 3: Testing with Postman + -Create a new GET request for the URI /api/courses. Add a test: status code of the request should be 200. Save the request to the _CourseAPI Tests_ collection +1) Create a new collection in Postman by selecting the *Collections* tab and clicking *New Collection*. Name it `CourseAPI Tests`. Launch the CourseAPI you have developed during the lectures, and make sure the `course_db` database is connected +2) Create a new `GET` request for the URI `/api/courses`. Add a test: status code of the request should be 200. Save the request to the `CourseAPI Tests` collection. +3) Create a new `GET` request for the URI `/api/courses/999999999`. Add a test: status code of the request should be 404. Save the request to the `CourseAPI Tests` collection. -Create a new GET request for the URI /api/courses/999999999. Add a test: status code of the request should be 404. Save the request to the _CourseAPI Tests_ collection +--- -Create a new POST request for the URI /api/courses. Set the headers and the content correctly, but set the value of credits to 100. Add a test: status code should be 400. Save the request to the _CourseAPI Tests_ collection + -Hover on CourseAPI Tests collection, click the arrow and click RUN. From the opened window, scroll down and click "Run CourseAPI Tests". This will create all the requests in your collection +4) Create a new `POST` request for the URI `/api/courses`. Set the headers and the content correctly, but set the value of credits to 100. Add a test; status code should be 400. Save the request to the CourseAPI Tests collection. +5) Hover on CourseAPI Tests collection, click the arrow and click *Run*. From the opened window, scroll down and click *Run CourseAPI Tests*. This will create all the requests in your collection. -# CI/CD/CT +## CI/CD/CT -* CI stands for _Continuous Integration_ +* CI stands for *__Continuous Integration__* * Each change in code triggers a build-and-test sequence for the given project - * Goal is to have a consistent and automated way to build, package, and test applications -* CD stands for _Continuous Delivery_ + * Goal is to have a consistent and automated way to build, package, and test applications +* CD stands for *__Continuous Delivery__* * Automates the delivery of applications to selected environments, such as Azure, AWS, GCP -* Both require _Continuous Testing_ , which includes automated regression, performance and other tests +* Both require *__Continuous Testing__*, which includes automated regression, performance and other tests diff --git a/imgs/9-testing_6.png b/imgs/9-testing_6.png index 9ec4762cd25d2edbfdaa364c055defb901b4a38f..b5147ae3ea89f84031a7fb060cd685b8b8c8957a 100644 GIT binary patch literal 41191 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 X$sT*WrF+%f8Gyjk)z4*}Q$iB}#fK~9