mattwhitfield / Unitverse

A unit test generation extension for Visual Studio that aims to always produce code that compiles - covering the basic cases automatically and preparing as much as it can for the complex cases.
MIT License
89 stars 20 forks source link

Small bug with nested code files #216

Closed Hefaistos68 closed 1 year ago

Hefaistos68 commented 1 year ago

Bug Description All right, not that easy to describe... Given a large code file, that was split into a few nested files, like shown here:

SuperDuperClass.cs
- SuperDuperClass.Public.cs
- SuperDuperClass.Private.cs
- SuperDuperClass.Protected.cs

When you create the unit tests for the SuperDuperClass.cs file, all is good and the SuperDuperClassTests.cs file is created. For the SuperDuperClass.Public.cs class, a SuperDuperClass.PublicTests.cs is created, and for all other files the tests are put into the same *.PublicTests.cs file, instead of each into its own*Tests.cs file.

Reproduce Create above file structure, put at least one method in each as a public partial class and then create tests for each file.

Additional context VS 2022 17.5.3, C# .NET6 project

Hefaistos68 commented 1 year ago

The private, public, protected filename parts can be replaced with whatever you want, as long as they share the same filename prefix.

Hefaistos68 commented 1 year ago

More about this: when I create empty files in the test project with the expected names and then use "Generate Tests" they go into the correct file. But, Unitverse does not recognize the tests as tests for extension class and creates code that does not compile:

    public partial class DateTimeExtensionsTests
    {
        private DateTimeExtensions _testClass;

        [TestInitialize]
        public void SetUp()
        {
            this._testClass = new DateTimeExtensions();
        }

Note: the partial keyword I inserted manually, Unitverse does not, it happily creates duplicate classes.

Hefaistos68 commented 1 year ago

There is a complication with file naming and nesting of tests: The tests for SuperDuperClass go into SuperDuperClassTests.cs, logically, to be able to nest the files correctly, the files for the nested sources should go into SuperDuperClassTests.Public.cs, SuperDuperClassTests.Private.cs, etc. This doesn't work well with the naming schemes currently. Files would be generated into SuperDuperClass.PublicTests.cs and such, which breaks nesting.

mattwhitfield commented 1 year ago

Thanks - I think I understand mostly. With the generation of code that doesn't compile - is it just that it is missing the partial keyword when generated? Or is there something else too?

Sorry if it seems a daft question - but I am on holiday at the moment so don't have access to code.

Hefaistos68 commented 1 year ago

So that makes two on holiday :) It's not only the partial keyword, it seems to miss the point that its a static class with only extension methods, which it normally nicely detects and handles. It also creates a static member of the class in the test class(es) and initialized it in the test setup. Which of course doesn't happen ever.

Hefaistos68 commented 1 year ago

Just remembered: when I turn off file nesting before generating tests, all is created fine in the right files, it also does not generate code that does not compile, does not try to create an instance of the static class.

mattwhitfield commented 1 year ago

Just to reiterate to make sure I understand:

Is that correct?

Hefaistos68 commented 1 year ago
  • When generating for partial files, we don't emit the partial keyword (in general)

True

  • When we have nested files, the naming is wrong, so that the tests don't end up nested as you'd expect

Not totally, only when the destination files don't exist. When you create the file structure manually, all tests go into the right files, and are discovered correctly. My guess is that the parent file of the nested files is automatically seen as folder, maybe a simple check if its a project folder or a file might solve it? After all now one can nest in the weirdest ways with the new nesting system. See FileNesting VS2022

  • When we have nested files that contain partial definitions of a static class, then we emit code that doesn't recognise the source as static

True

Hefaistos68 commented 1 year ago

SuperDuper.zip

made a small test project. When you create a test project for it, and then create unit tests for any of the nested files, unitverse will create a folder called "SuperDuper.cs" and under it the unit tests. When you create tests for the "SuperDuper.cs" file, they go into the previously created "SuperDuper.xxxx.cs" file, although, if the corresponding file exists (i did create them manually) the tests go into the correct file.

mattwhitfield commented 1 year ago

Ok, I've definitely made some progress on this. Not quite ready for release yet - but there were a couple of things happening:

1) The naming was incorrect - got that nailed. 2) It was treating the parent file as a folder - it now creates the file in the right place. 3) It was placing content in the wrong file. This is actually an effect of a feature - in that it's finding an existing file with the right test class name, even if the file naming is not correct. I've disabled that behaviour for types that are defined in multiple files. 4) It wasn't emitting the partial keyword - it now does.

The one thing I haven't sorted yet is that if you generate for multiple files - it will put a [SetUp] (or equivalent) in each one. I am not sure if I will try and fix that - because it might be a really big refactor.

Anyway, didn't want to just leave it without an update 👍

Hefaistos68 commented 1 year ago

Sounds like good progress. Nice. Not sure if that [SetUp] is much of an issue. But if you are using Roslyn it might be a relatively easy fix, load up the related files, check for that setup/initialize attribute in any member, etc.

mattwhitfield commented 1 year ago

It is less the 'have I emitted a setup method in another part' and more 'there are some tests that assume that certain things are set up (e.g. property initialization tests) - do the same assumptions still hold true?'.

Also, during generation, the target currently isn't inspected. It is only after generation is complete that the generated content is created in the target. So there would be some fun there, too.

Hefaistos68 commented 1 year ago

Actually, I think that a viable solution would be to have sub-namespaces created for nested files. As we are talking about independent tests, this would not hurt and it would allow for test setup/initialization in each file. Also removes the need for the partial keyword. like:

namespace SuperDuperTests
namespace SuperDuperTests.NestedFile1Tests
namespace SuperDuperTests.NestedFile2Tests

should work

mattwhitfield commented 1 year ago

I'm going to go with partial for now - trying to cram too much into one issue confuses me :)

mattwhitfield commented 1 year ago

0.192 should address this - let me know if it works for you 👍

Hefaistos68 commented 1 year ago

Not sure if I get to it this weekend, latest on Monday.

Hefaistos68 commented 1 year ago

So far seems to work fine.

mattwhitfield commented 1 year ago

Cool, thanks for the feedback