TCR - Advanced

PREAMBLE (written after the fact)

My next series of posts is probably going to be about writing my magic card site for the nth time.
The goal is to do strict TDD using TCR. I did a little of it tonight... and it's 1am... so... I'm having fun with it. I can run fast and do big; but the small focused changes just lands different.

Anyway; if you wanna check out the TCR https://github.com/Fyzxs/DotNetCoreTcr

Otherwise... suffer my madness.

MtgDiscovery v3.5v3.

This is the 3rd-ish attempt at re-doing the MtgDiscovery site for the 3rd time. This time I have an idea that's not just "write it again". I'm gonna hurt myself.

My plan; at the high level is to do ALL OF THE GREAT THINGS I want to be doing... aaaaannnnnddddd am often too lazy to do properly.

The biggest is doing TDD Like I Mean it. As best as I can. Which ... can be a bit challenging to remember to do when solo. Which means I need tools to help me.

One of them is TC||R. I'll update my script and use that to ensure I make VERY TEENY steps.
Since I'm going to do VERY TEENY steps, Arlo's Commit Notation seems like it'll be great to use.

I have a TC||R script; but I don't have anything to help me commit early and often. So... I'm gonna build a Visual Studio extension that'll help me commit early and often.

New To Extensions

Since I've never done a VS extension before... heavy CoPilot usage.

OK... Hated that experience. Pretty sure Visual Studio does not have a way to hook into a test runner finish.

Anyway... like most extension building; help is near unavailable.

Other stuff

Let's move onto the actual work and we'll see what tooling we need to build.

Creating the folder and a git init.
First gonna add Visual Studio .gitignore
Since I'm gonna use Arlo's Commit Notation might as well start

... OK; got a bunch of base files created.

Next Day

I'm currently working on a script to detect changes and run the tests. Which... I spent a bunch of time on last night.

I realized this is basically what my TC||R script does... so... gonna crib that for a bunch.

What I want to do; thanks to James Shore at AONW; is to minimize the tests required to run. It's still gotta build; but that's OK.
So now; gonna put some some logic into checking what was changed; if not a test file, then use naming convention to run just the tests for that class.

It should be fun.

The logic is; basically

  • Walk up the directories until we hit a csproj file.
  • If it's a .Tests.csproj file; run the tests filtered to that file name
  • If it's a .[^Tests].csproj file; add .Tests and run the changed file with Tests suffixed.

That's the plan... now to make it work.

Next Day

That worked. I also got a dialog box to help with ACN.

Now it's onto the coding... which I need to figure out what I want to do.

Now I'm hung up on what tool to use to track the shit I want to do. Gonna try Miro and do a disocovery map.

The first piece I'm gonna do is to load the bulk cards into a cosmos collection.

Not really sure what I'm gonan do... so I'm gonna start with a test project "Lib.Starter.Tests" and... uhh... go from there? ... I guess...

Do I want to start with writing to cosmos; or processing the file... Let's try the cosmos route. I think it'll be interesting.

A few days later

I have been thinking about it; and I think I am gonna go the "write to cosmos route". I'm gonna encapsulate it all behind a "ICosmosScribe". Later I'll get some implementation of it.

Looking at my projects though... I need to update my Directory.Build.Props to manage some stuff for me.

While I wait for some work computer stuff to calm down... I'll start to TCR the CosmosItem; which is the foundation for all of my cosmos reads/writes.

The Discovery Tree I'm starting with looks like this <DiscoveryTree_CosmosItem.png>... but I realized that's not gonna work quite right.

Also TCR isn't gonna work QUITE right the way I normally TDD... which is to do everything in the test class until I move it to the real location... I have to jump ahead to that for TCR to really work... Maybe... that can be a refactor step... let's see.

...
I guess the first step is just to create the CosmosItem we can add more tasks around the creation of the Lib.Cosmos project later.

OK - first set of TCRing
Renaming the test file - which failed. Not sure why... might be nice to dig that out later. - ahh; file change was a temp file.

OK, updated script - now it says there's no change to commit even though I renamed a test file...

I had to do a Set-Location in the script so git runs in the right spot.

booo... I don't have good test templates set up.
Added the template

[TestMethod, TestCategory("unit")]
public void $METHOD$()
{
    //arrange

    //act

    //assert
    $END$
}

If there's something you can do to make your development life smoother... do it. Without... ya know... sacrificing things for the short term.

This is my first test

    [TestMethod, TestCategory("unit")]
    public void CosmosItem_Should_Exist()
    {
        //arrange

        //act
        new CosmosItem();

        //assert

    }

Which fails the build.

I wonder what the correct ACN is for the following...

[TestClass]
public sealed class CosmosItemTests
{
    [TestMethod, TestCategory("unit")]
    public void CosmosItem_Should_Exist()
    {
        //arrange

        //act
        new CosmosItem();

        //assert

    }
}

public /* cosmos requires */ class CosmosItem
{
}

I had the tool create the class for me... it's a provable refactor... so... . r but it's a new feature... so F... at least f.
Is there ever really an f? I did '. r'. But I'm wondering if that's correct. It's... blurry in the early days?

Anyway... gotta create the project so the revert will actually hurt.

huh... it says it reverts... but I'm not seeing any revert... ... it's not reverting new files...
I'm gonna let this slide for now. Creating a new project and moving the file broke it. I wish I could make it revert stuff... but it's a gray area. What I'm going to do is a little self-discipline; and add the project reference THEN move the class.
Hmm... I think adding a new project is gonna need to be a commit... I think I cancelled when it tried to do it for me... whoopsie

huh... it does not try to commit if I add a project.

Digging in; looks like a timing issue. Gonna add a check for the failure output.

.... Ahhhh.... looks like my mistake. Obviously.

The issue is that I didn't create the Lib.Cosmos.Tests project first. ... and now my revert is much more aggressive. If it doesn't work; untracked folders are blown away.

Now; steps to create a new project; create test project, create project.
Once that's done, to move a class, add reference project to move to, then add namespace, then move class out.

AAAANNNNNDDDD... now I need to sneak in my Directory.Build.props update...

OK... gonna kinda roll back. Especially with the new props file... the process of test projects before real projects; and there being a 1:1 match have to happen. This is specific to the Directory.Build.props file I use. (I auto include the project with .Tests removed.)

OK; back to just a sln; TCR running, now to create Lib.Cosmos.Tests.
It's angry because there's no corresponding non-test project. ... and this time I'm gonna do it like
... hah... it gets angry when the tests don't compile ...

Hrm... it's nuking them because I'm using Centrally managed packages and the project creation doesn't. I think for now; project creation is going to have to be outside of TCR.

trying stuff

dotnet new mstest -f net9.0 --sdk --test-runner VSTest --coverage-tool coverlet --extensions-profile AllMicrosoft --fixture None --name Lib.Cosmos.Tests

dotnet new classlib -f net9.0 --no-restore --name Lib.Cosmos

dotnet sln .\MtgDiscoveryTdd.sln add --in-root .\Lib.Cosmos.Tests\Lib.Cosmos.Tests.csproj

function CreateProjectPair($projectName) {
    Write-Host "Creating project pair for: $projectName"
    
    $projectPath = ".\$($projectName)\$($projectName).csproj"
    $projectTestPath = ".\$($projectName).Tests\$($projectName).Tests.csproj"

    # dotnet new classlib -f net9.0 --no-restore --name $projectName
    # New Project
    New-Item -Path $projectPath -ItemType File -Force
    Add-Content -Path $projectPath -Value '<Project Sdk="Microsoft.NET.Sdk"></Project>'



    # dotnet new mstest -f net9.0 --sdk --test-runner VSTest --coverage-tool coverlet --extensions-profile AllMicrosoft --fixture None --name "$($projectName).Tests"
    # New Test Project
    New-Item -Path $projectTestPath -ItemType File -Force
    Add-Content -Path $projectTestPath -Value '<Project Sdk="MSTest.Sdk/3.6.4"></Project>'

    dotnet sln .\MtgDiscoveryTdd.sln add --in-root .\$($projectName)\$($projectName).csproj
    dotnet sln .\MtgDiscoveryTdd.sln add --in-root .\$($projectName).Tests\$($projectName).Tests.csproj
}

OK... I can pause my script so I can create projects

OK - the above CreateProjectPair function works without the TCR being paused. Let's see what happens when I create a testclass

Note to self... commit after adding the projects...

OK; recreated the project pair, now gonna create a class, using the MSTest Template.

TCR isn't happy - but that's OK; it doesn't blow it away yet.

I create the test class, CosmosItemTests, I can write failing tests, I can move the class to non-test project (which oddly worked... not 100% why...)

I was able to add NewtonSoft to the main project while trying to add the JsonProperty for serialization. It nuked it because the csproj file changed; but the SLN had reference to it, so next time it seemed to work.
I'm happy taking a simple double on letting visual studio add a nuget package for me.

Aside from some of the early putzing with the script to get it working... this is... so far... fun. For like 3 tests.

One thing I was worried about was not being able to go red. On the simpler stuff, it fails. "right reason"... ehhhh; maybe. I can see some where the failure will blow it away... but then I do the correct implementation. So... I can SEE it fail... I just can't commit it as failing.

A few hours later

It's midnight... I have one stupid ass databag coded

public class CosmosItem
{
    private string _itemType;

    [JsonProperty("id")]
    public virtual string Id { get; set; }

    [JsonProperty("partition")]
    public virtual string Partition { get; set; }

    [JsonProperty("item_type")]
    public string ItemType
    {
        get => _itemType ??= GetType().FullName;
        set => _itemType = value;
    }

    [JsonProperty("created_date")]
    public string CreatedDate { get; set; } = DateTime.UtcNow.ToString("o");
}

But... HAHAHAHA - it's so fun. TCR having a popup to do the commit on tests passing... beautiful.

Th TCR blowing away non-test files when I do it wrong... keeps me freakin' honest in my approaches. Some stuff I still have to make sure to do it right. I almost changed two method names before saving the file and causing a commit to happen. ALMOST.

Gonna post this as my adventures in TCR begin anew...

Oh - for the script; go to DotNetCoreTcr