<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jack Steel</title>
    <description>The latest articles on Forem by Jack Steel (@jacksteel97).</description>
    <link>https://forem.com/jacksteel97</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F390346%2Fbcd98783-bd42-44dc-9de3-840aae47a0a9.png</url>
      <title>Forem: Jack Steel</title>
      <link>https://forem.com/jacksteel97</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jacksteel97"/>
    <language>en</language>
    <item>
      <title>Mutation Testing - A .NET Developer's Guide</title>
      <dc:creator>Jack Steel</dc:creator>
      <pubDate>Sat, 07 Oct 2023 13:05:00 +0000</pubDate>
      <link>https://forem.com/jacksteel97/mutation-testing-a-net-developers-guide-2fhj</link>
      <guid>https://forem.com/jacksteel97/mutation-testing-a-net-developers-guide-2fhj</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Mutation Tests allow us to evaluate and quantify how good our Unit Tests are. Your true 'test effectiveness score' could be thought of as &lt;code&gt;Code Coverage Percentage * Mutation Score Percentage&lt;/code&gt; you can have 100% code coverage, but if your mutation score is 0% then all of those fancy tests are actually most likely worthless. In other words, Mutation Tests are tests for your tests.&lt;/p&gt;

&lt;p&gt;Let's look at an example to illustrate how this works in practice.&lt;/p&gt;

&lt;p&gt;Consider the following code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and this Unit Test&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Theory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;InlineData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;InlineData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CanAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;expectedResult&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of you may have already spotted the problem with this test (gold star for you!). &lt;br&gt;
Let's run the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 + 0 = 0 PASS
2 + 2 = 4 PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome, looks good to me!&lt;/p&gt;

&lt;p&gt;Now imagine someone messes up a find-and-replace, or somehow makes the mistake of changing the &lt;code&gt;Add&lt;/code&gt; method's code to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, you might assume that this is an obvious mistake that would easily be caught by unit tests failing and quickly fixed by the developer.&lt;br&gt;
However, if we run the unit tests again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 * 0 = 0 PASS
2 * 2 = 4 PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All our tests are still passing, but the behaviour of the system has fundamentally changed and in this particular case is &lt;strong&gt;definitely broken&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is exactly what Mutation Tests do, they make small changes to the source code (a Mutant) and then run our Unit Tests against the changed code, if the Mutation causes a test to fail, then we've successfully &lt;strong&gt;Killed&lt;/strong&gt; that Mutant (this is good), however if all our tests still pass then the Mutant has escaped and &lt;strong&gt;Survived&lt;/strong&gt; (this is bad). The above example is somewhat unrealistic and trivial but I hope you can see how this concept extends to much more complex systems.&lt;/p&gt;

&lt;h1&gt;
  
  
  Enter Stryker
&lt;/h1&gt;

&lt;p&gt;There are many different tools for doing mutation testing but &lt;a href="https://stryker-mutator.io/"&gt;Stryker Mutator&lt;/a&gt; is the standard usually used for .NET.&lt;br&gt;
You can read more about it &lt;a href="https://stryker-mutator.io/docs/stryker-net/introduction/"&gt;here&lt;/a&gt;&lt;br&gt;
and try out an interactive example similar to the scenario described above &lt;a href="https://stryker-mutator.io/stryker-playground/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stryker has many mutation types, you can see a list of them all &lt;a href="https://stryker-mutator.io/docs/stryker-net/mutations/"&gt;here&lt;/a&gt; it can do simple operator swaps like the one in the example above switching &lt;code&gt;+&lt;/code&gt; for &lt;code&gt;*&lt;/code&gt;, all the way up to removing entire blocks of code, or altering Regular Expression syntax&lt;/p&gt;
&lt;h2&gt;
  
  
  Making It Useful
&lt;/h2&gt;

&lt;p&gt;Mutation Tests, by their very nature, are a slow process. For small libraries, this probably isn't a big deal - adding 30 seconds to your pipeline is no problem (yes I'm assuming you've got a test pipeline). However, if we want this to scale to enterprise software (and we do), you're gonna have more than 30 seconds of run time for mutation tests.&lt;/p&gt;

&lt;p&gt;The largest service I had access to run this against generates about 3,000 mutations and, when running in a pipeline, takes about 4 hours to complete. If you're doing multiple releases per day then adding even 30 minutes to a build pipeline is a major problem, not to mention it sitting occupying one of your available build agents for a significant amount of time. You'll quickly get complaints from other devs that their changes are being delayed by your multiple-hour pipelines. Our target for pipeline duration is about 15 minutes, from a change being merged to it running in Production should take no longer than 15 minutes, so clearly we can't run mutation tests as a quality gate on deployments.&lt;/p&gt;

&lt;p&gt;If we can't put this in as a deployment quality gate what are our options?&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 1 - Run it Locally
&lt;/h3&gt;

&lt;p&gt;We can advise it as a dev-run process that each dev should run mutation tests locally when they're happy with their change and ready to merge and hope that they pay attention to the results if they introduce code that drops the mutation score off a cliff.&lt;/p&gt;

&lt;p&gt;This has the obvious downside of relying on people to follow the process (in my experience, this would likely be forgotten after a week or two) and now we're back to square one.&lt;/p&gt;

&lt;p&gt;The above is exacerbated by mutation tests being slow, no one wants to have to remember to run another tool locally, especially if that tool has the ability to pin their CPU for an hour.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Aai11FKB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://imgs.xkcd.com/comics/compiling.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Aai11FKB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://imgs.xkcd.com/comics/compiling.png" alt="Relevant XKCD" width="413" height="360"&gt;&lt;/a&gt;&lt;br&gt;
Although I do like the idea of this excuse, I don't think it's worth a return to extremely long compilation jobs (sorry Rust™ devs).&lt;/p&gt;

&lt;p&gt;Another problem with this approach is observability. Unless we add another step in the process of dumping the report somewhere shared for historical stats we lose all ability to build pretty graphs, and isn't that half the point??&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 2 - A new Pipeline
&lt;/h3&gt;

&lt;p&gt;No one ever said we had to have one test pipeline per service. If we split the mutation tests into their own pipeline that just runs mutation tests and pipes the results off to other services for analysis then that might be the best we can do until someone comes up with some sort of quantum-everything-at-once-mutation-tests that makes them Blazingly Fast™.&lt;/p&gt;

&lt;p&gt;We still can't use them as a quality gate on deployments but we can use them as a quality alert, if the mutation score takes a nosedive over a given timeframe we can alert the relevant team that they need to take a look at it.&lt;/p&gt;

&lt;p&gt;We can run this new pipeline on any schedule that makes sense for your application, if you're making changes frequently maybe you want to run it nightly. If your services can go a while without much active development on individual services then maybe weekly or more is more appropriate. We've gone with weekly on Sunday nights for now, maybe that will change in the future as we see how the stats change over time. So devs get to log in on Monday morning and be greeted with some (hopefully in the green) stats and pretty graphs.&lt;/p&gt;

&lt;p&gt;If any of this sounds interesting and you want to give it a go, take a look at the sections below for Setting Up Stryker For The First Time or Using Stryker.&lt;/p&gt;
&lt;h1&gt;
  
  
  Setting Up Stryker For The First Time
&lt;/h1&gt;

&lt;p&gt;This section covers setting up Stryker for the first time on a new or existing project, you should only need to do this once per project, after that it should Just Work™&lt;/p&gt;

&lt;p&gt;Note: All of the commands in this guide are being run from your solution's root directory (the same directory your &lt;code&gt;.sln&lt;/code&gt; file is in)&lt;/p&gt;

&lt;p&gt;We're going to install the Stryker Tool in the project's tool manifest, this ensures that everyone else can easily install and update Stryker on that project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet new tool-manifest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, that should have created the &lt;code&gt;dotnet-tools.json&lt;/code&gt; file, most likely in the &lt;code&gt;.config&lt;/code&gt; directory&lt;br&gt;
&lt;strong&gt;Check this file into source control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can now actually install Stryker locally with the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet tool &lt;span class="nb"&gt;install &lt;/span&gt;dotnet-stryker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a success message about Stryker being installed and added to the tool manifest&lt;/p&gt;

&lt;p&gt;Finally, we want to configure Stryker to run correctly on our project. In the same directory as your &lt;code&gt;.sln&lt;/code&gt; file add a new file named &lt;code&gt;stryker-config.json&lt;/code&gt;. Then paste in the content below and I'll walk you through what &lt;strong&gt;needs&lt;/strong&gt; to be changed and what you can configure to your preference&lt;br&gt;
&lt;strong&gt;Check this file into source control&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stryker-config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"solution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YourSolution.sln"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target-framework"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"net6.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mutation-level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Complete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reporters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"progress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"markdown"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"additional-timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"thresholds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"break"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"coverage-analysis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"perTestInIsolation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration Explanation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;This &lt;strong&gt;MUST&lt;/strong&gt; be the exact name of your &lt;code&gt;.sln&lt;/code&gt; file. It must not include any filepath &lt;code&gt;/&lt;/code&gt;s or &lt;code&gt;..&lt;/code&gt;s it is not the filepath to your solution it is just the filename.&lt;/p&gt;

&lt;h3&gt;
  
  
  Target-Framework
&lt;/h3&gt;

&lt;p&gt;If your project supports multiple build targets and this config is not set Stryker will pick a random build target from your options each time it runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mutation-Level
&lt;/h3&gt;

&lt;p&gt;This defines the amount of mutation you want Stryker to do, for the best results this should be set to &lt;code&gt;Complete&lt;/code&gt; or &lt;code&gt;Advanced&lt;/code&gt; when committing to source control. However, you may want to lower it to &lt;code&gt;Basic&lt;/code&gt; or &lt;code&gt;Standard&lt;/code&gt; when running locally to save yourself time (if the mutation you're investigating is in those levels). See &lt;a href="https://stryker-mutator.io/docs/stryker-net/configuration/#mutation-level-level"&gt;here&lt;/a&gt; for more details on the possible options: &lt;/p&gt;

&lt;h3&gt;
  
  
  Reporters
&lt;/h3&gt;

&lt;p&gt;This array defines any report formats you want to generate, you probably want &lt;code&gt;html&lt;/code&gt; for user-friendliness and &lt;code&gt;progress&lt;/code&gt; for progress visibility when running, and then maybe &lt;code&gt;json&lt;/code&gt; if you're doing anything fancy with the output like we are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Timeout
&lt;/h3&gt;

&lt;p&gt;Some mutations can cause infinite loops. The timeout to cancel a test is the &lt;code&gt;initialTestTime + additionalTimeout&lt;/code&gt; if you run into problems with tests that vary in running time significantly then you may want to increase this setting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thresholds
&lt;/h3&gt;

&lt;p&gt;This defines our expectations for the mutation score on this project, much like code coverage thresholds you can set high, low, and break. If the mutation score is below the break threshold then Stryker will exit with a non-zero exit code and fail the pipeline.&lt;br&gt;
They also define the boundaries used for colour coding the mutation report.&lt;/p&gt;
&lt;h3&gt;
  
  
  Coverage-Analysis
&lt;/h3&gt;

&lt;p&gt;You probably want to use &lt;code&gt;perTestInIsolation&lt;/code&gt; for most cases.&lt;br&gt;
You can read the details of what each setting does &lt;a href="https://stryker-mutator.io/docs/stryker-net/configuration/#coverage-analysis-string"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;code&gt;perTestInIsolation&lt;/code&gt; strikes a balance of best accuracy and fastest run time at the expense of a longer start-up analysis time.&lt;/p&gt;
&lt;h1&gt;
  
  
  Using Stryker
&lt;/h1&gt;

&lt;p&gt;Once Stryker has been set up and your pipeline and configuration are merged to the main branch, you (and any other devs) can now use it to their heart's content.&lt;/p&gt;
&lt;h2&gt;
  
  
  Locally
&lt;/h2&gt;

&lt;p&gt;If you're cloning a project that includes mutation test configuration for the first time then you'll need to run the following command to install Stryker locally for that project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet tool restore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then to run Stryker make sure your terminal working directory is the same directory your &lt;code&gt;.sln&lt;/code&gt; file is in (&lt;a href="https://stryker-mutator.io/docs/stryker-net/operating-modes/"&gt;see also&lt;/a&gt;) and run the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet stryker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Easy as that, you should soon see logs of Stryker working on analysing your project.&lt;/p&gt;

&lt;p&gt;If you're trying to diagnose a configuration issue or problem getting Stryker working I recommend running with trace log verbosity&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dotnet stryker &lt;span class="nt"&gt;--verbosity&lt;/span&gt; trace &lt;span class="nt"&gt;--log-to-file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gotchas and Good-to-Knows
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mutant States and Scores
&lt;/h3&gt;

&lt;p&gt;There are several states a mutant can end up in. However, for the mutation score calculation, only a few are relevant.&lt;br&gt;
The mutation score formula is &lt;code&gt;Score = DetectedMutants / (DetectedMutants + UndetectedMutants)&lt;/code&gt;&lt;br&gt;
Where &lt;code&gt;DetectedMutants&lt;/code&gt; is the number of Mutants that were &lt;code&gt;Killed&lt;/code&gt; or &lt;code&gt;Timed Out&lt;/code&gt; and &lt;code&gt;UndetectedMutants&lt;/code&gt; is the number of Mutants that &lt;code&gt;Survived&lt;/code&gt; or had &lt;code&gt;No Coverage&lt;/code&gt;.&lt;br&gt;
This means that &lt;code&gt;No Coverage&lt;/code&gt; mutants can lower the overall mutation score, if you have excluded any tests from being run during Mutation Testing then any code they cover could be mutated and produce a &lt;code&gt;No Coverage&lt;/code&gt; mutant.&lt;br&gt;
If you have a lot of these types of tests and can't cover the code with Unit Tests that can be run in a mutation context then this is important to keep in mind when evaluating scores. You should take a look at the &lt;code&gt;Survived&lt;/code&gt;, &lt;code&gt;Killed&lt;/code&gt;, and &lt;code&gt;No Coverage&lt;/code&gt; counts more closely in the report rather than just the overall percentage score.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failures in CI
&lt;/h3&gt;

&lt;p&gt;Sometimes your pipeline run may fail due to an error along the lines of "&lt;code&gt;System.Attribute&lt;/code&gt; is not defined". This is an ongoing (and extremely annoying) bug with Stryker or one of its dependencies (&lt;a href="https://github.com/stryker-mutator/stryker-net/issues/931"&gt;https://github.com/stryker-mutator/stryker-net/issues/931&lt;/a&gt;) with an unknown root cause. Some of these options may help you get the pipeline working again:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;disable-mix-mutants&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Turn on log &lt;code&gt;verbosity&lt;/code&gt; &lt;code&gt;trace&lt;/code&gt; in the config file and check which files are logged in relation to this error, then ignore mutations in those files using the &lt;code&gt;mutate&lt;/code&gt; configuration option.
If none of those help, it may be intermittent enough that you can just re-run the pipeline a couple of times.
If the pipeline consistently fails then please check the Stryker version being used, this problem has been observed with version &lt;code&gt;3.10.0&lt;/code&gt;, hopefully, it will be fixed in later versions (which may be released by the time you read this)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  In Closing
&lt;/h1&gt;

&lt;p&gt;Mutation tests are extremely helpful for ensuring you maintain a high quality of tests over time. Requirements change, old tests get forgotten about, people leave and the context of features can be lost. It is easier than you'd expect to end up with a collection of tests that fly under the radar, always passing but no longer actually testing what they should. In the best case, this adds some necessary computation to your test runs, in the worst case these false positives give you false confidence in your changes.&lt;/p&gt;

&lt;p&gt;Mutation testing can help catch bad tests. They will probably never be fast enough to put in as a quality gate on any service that you want to release hyper-frequently but they provide a useful metric to objectively evaluate the quality of your tests. Often we accept sub-par code in tests when reviewing code changes because "they're just tests"; mutation testing can help you find out if any actually problematic code has made it through review because of this more lax approach.&lt;/p&gt;

</description>
      <category>stryker</category>
      <category>dotnet</category>
      <category>testing</category>
      <category>learning</category>
    </item>
    <item>
      <title>Nice-Numeric-Input</title>
      <dc:creator>Jack Steel</dc:creator>
      <pubDate>Mon, 20 Sep 2021 16:26:50 +0000</pubDate>
      <link>https://forem.com/jacksteel97/nice-numeric-input-45fb</link>
      <guid>https://forem.com/jacksteel97/nice-numeric-input-45fb</guid>
      <description>&lt;p&gt;Here's what I've been working on for the past week or so - my first ever public NPM package. &lt;a href="https://github.com/JackSteel97/Nice-Numeric-Input"&gt;https://github.com/JackSteel97/Nice-Numeric-Input&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice-Numeric-Input is a modern, rich featured and highly customisable numeric input built on Vue. Capable of formatting as the user types, including currency formatting. With no extra dependencies other than Vue itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codepen.io/Jsteel97/pen/YzQLZeP"&gt;Basic Demo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;install &lt;/span&gt;nice-numeric-input
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Import&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NiceNumericInput&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nice-numeric-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Register&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NiceNumericInput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a full list of props see the Reference Props section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nice-numeric-input&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"cashValue"&lt;/span&gt;
                    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter a cash value"&lt;/span&gt;
                    &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Cash"&lt;/span&gt;
                    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"example"&lt;/span&gt;
                    &lt;span class="na"&gt;currency=&lt;/span&gt;&lt;span class="s"&gt;"GBP"&lt;/span&gt;
                    &lt;span class="na"&gt;locale=&lt;/span&gt;&lt;span class="s"&gt;"en-GB"&lt;/span&gt;
                    &lt;span class="na"&gt;:max=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt;
                    &lt;span class="na"&gt;:min=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Props
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prop&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Default Value&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;v-model&lt;/code&gt; to get two-way binding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"nice-numeric-input"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"nice-numeric-input"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;label&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Required for accessibility, use &lt;code&gt;hide-label&lt;/code&gt; to remove the visual label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;placeholder&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;step&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The amount to increase or decrease when using the step buttons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;min&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NEGATIVE_INFINITY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anything entered below this will fail the &lt;code&gt;isValid&lt;/code&gt; check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POSITIVE_INFINITY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anything entered above this will fail the &lt;code&gt;isValid&lt;/code&gt; check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isValid&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Basic internal validation, use the &lt;code&gt;.sync&lt;/code&gt; modifier to get changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;disabled&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable the entire component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;locale&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;`window.navigator.language \&lt;/td&gt;
&lt;td&gt;\&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;currency&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;String&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;null&lt;/code&gt; currency formatting is turned off. Otherwise it can be any currency code e.g. &lt;code&gt;USD&lt;/code&gt;, &lt;code&gt;EUR&lt;/code&gt;, &lt;code&gt;JPY&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;minDecimalPlaces&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;maxDecimalPlaces&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;integerOnly&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prevents decimal numbers being entered, entries are rounded to the nearest integer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;noControls&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes the step buttons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hideLabel&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes the visual label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decreaseTitle&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"Decrease"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The button title for accessibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;increaseTitle&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"Increase"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The button title for accessibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;increaseText&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"+"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The text displayed on the increase step button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decreaseText&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"-"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The text displayed on the decrease step button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;superIncreaseText&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"++"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The text displayed on the increase step button when super step is active (Ctrl or Alt)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;superDecreaseText&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"--"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The text displayed on the decrease step button when super step is active (Ctrl or Alt)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ultraIncreaseText&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"+++"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The text displayed on the increase step button when ultra step is active (Ctrl + Alt)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ultraDecreaseText&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"---"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The text displayed on the decrease step button when ultra step is active (Ctrl + Alt)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;superStep&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The amount to change during a super step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ultraStep&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The amount to change during an ultra step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;labelClass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;A custom class to apply to the visual label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;inputClass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;A custom class to apply to the input field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decreaseButtonClass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;A custom class to apply to the decrease step button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;increaseButtonClass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;A custom class to apply to the increase step button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wrapperClass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;A custom class to apply to the top level component element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;superStepClass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;A custom class to apply to both step buttons when super step is active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ultraStepClass&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;A custom class to apply to both step buttons when ultra step is active&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Emitted Type&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;input&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Used automatically by a &lt;code&gt;v-model&lt;/code&gt; binding. Can be bound manually, fires when the value is changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;update:isValid&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Used automatically by the &lt;code&gt;.sync&lt;/code&gt; modifier on &lt;code&gt;isValid&lt;/code&gt; prop. Can be bound manually, fires when the valid value changes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>vue</category>
      <category>typescript</category>
      <category>npm</category>
    </item>
    <item>
      <title>Analysing Student Timetable Data for Smarter Routing</title>
      <dc:creator>Jack Steel</dc:creator>
      <pubDate>Wed, 20 May 2020 09:07:36 +0000</pubDate>
      <link>https://forem.com/jacksteel97/analysing-student-timetable-data-for-smarter-routing-23ob</link>
      <guid>https://forem.com/jacksteel97/analysing-student-timetable-data-for-smarter-routing-23ob</guid>
      <description>&lt;p&gt;A summary of my 2020 dissertation project, built with &lt;code&gt;C#&lt;/code&gt;, &lt;code&gt;Vue.JS&lt;/code&gt;, and &lt;code&gt;ASP.NET Core&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;This project presents a successful proof of concept design and a successful implementation of the A* pathfinding algorithm incorporating pedestrian congestion. The University of Lincoln’s campus and student timetable data was used to develop this system. It has been shown that considering congestion during the pathfinding process can provide better results for those wishing to navigate the campus more efficiently. Congestion adjusted routes can achieve a time-saving of over 70% for a user navigating the campus on common routes.  Analysing the congestion around campus using a heatmap also provides insight into the locations of bottlenecks on pedestrian movement. This project has laid the groundwork for future developments in this area and highlighted several key areas for further work, including further automation of the pathfinding graph generation and investigations into student behaviours to improve the model’s accuracy. Importantly, this project has shown that the improved routing algorithm is capable of being processed within an acceptable timeframe for interactive use. All development was completed with reuse and extension in mind for any further work or adaptation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Demonstration
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P0DxEa9c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/WX1XwaE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P0DxEa9c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/WX1XwaE.png" alt="alt text" title="Demo Screenshot" width="800" height="669"&gt;&lt;/a&gt;&lt;br&gt;
The above screenshot shows an example of the system in action. The route requested is starting at Cygnet Wharf Block A accommodation, the destination is INB0114 – the main lecture theatre in the Isaac Newton Building, and the start time for the route is set to 13:00 (1 pm). Clearly shown in the two route details sections are the distance and estimated durations of these routes. The estimated duration for both takes congestion into account, however only the adjusted route took congestion into account when calculating the shortest path. It can be seen here that even though the adjusted route is over 70 metres longer than the standard route, it is almost 10 minutes faster due to congestion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8ZLuaCtz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/KgqaTYo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8ZLuaCtz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/KgqaTYo.png" alt="alt text" title="Heatmap Demo" width="800" height="492"&gt;&lt;/a&gt;&lt;br&gt;
The above screenshot shows the map view with congestion view turned on - this provides a heatmap of the estimated congestion throughout the route. It is clear to see that the congestion along the standard route (shown by the green line) is worst around the entrances to the Arts Bridge. This congestion is likely the main slowdown for this route that causes the adjusted route (shown by the orange line) to be faster. The adjusted route has done an excellent job of avoiding any hotspots of congestion, by routing over the much less used Brayford Way bridge for most of the distance.&lt;/p&gt;

</description>
      <category>dissertation</category>
      <category>csharp</category>
      <category>gitgraduation</category>
      <category>analysis</category>
    </item>
  </channel>
</rss>
