RandomEngy / UnitTestBoilerplateGenerator

An extension for Visual Studio that generates a unit test boilerplate from a given class, setting up mocks for all dependencies. Supports NUnit, Visual Studio Test, XUnit and many mock frameworks.
https://marketplace.visualstudio.com/items?itemName=RandomEngy.UnitTestBoilerplateGenerator
MIT License
64 stars 17 forks source link

New Main Content Token #24

Open jchesshir opened 2 years ago

jchesshir commented 2 years ago

Installed Product Versions

Description

The following token would be another nice addition to your main content section.

The particular use case I'm working with involves mimicking existing SUT functionality in an API level Constructor for our Business Level Tests. The constructor calls a function in the Business Level Base class. This Business Level function, if called, changes the default behavior of several of our MUTs (Methods Under Test). But we have to remember to call it EVERY time we generate a new Test class.

The complexity comes in the fact that this function automatically calls another function in the Database Level Base Interface, which is the base interface of every single Database interface that we have to mock. We are also using Strict detection, so the Database function will have to have to have an arrangement set up for it before the SUT's method is called. We know that each interface will be the first one that the UTBG picks up, but it will be one that is only known at generation time. We cannot merely name the base Interface and arrange it. We have to know the name of the mock object at code generation time.

This single new token would give us the flexibility we need to set up the template to automatically set up this call as part of the SUT initialization:

Scope Token Description Example
Main Content $MockField[<n>]$ The name of a generated mock field token where <n> is the field index of the token.
Also usable in the Test Method Invoke/Empty templates.
$MockField[0]$.baseClassFunction(); ->
mockChildInterface.baseClassFunction();
Main Content $MockField
<InterfaceOrClassName>$
The name of a generated mock field token where InterfaceOrClassName is the Class Name, a Base Class it inherits, or an Interface/Base Interface it implements.
Also usable in the Test Method Invoke/Empty templates.
$MockField<BaseInterface>$
.baseFunction(); ->
mockChildInterface.baseFunction();
Mock Field Formats $MockField$ The generated name for the mock object.
Specified by "Mock object reference format" field.
$MockField$.Object ->
mockChildInterface.Object
jchesshir commented 2 years ago

@RandomEngy I'm willing to implement if you're good with it.

Thanks!

RandomEngy commented 2 years ago

Seems fine with me. You'll probably also need to introduce a new format: "Mock field name" that defaults to mock$InterfaceMockName$, so you can actually get the mock field name cleanly. Then you could change the defaults for "Mock field declaration format", "Mock field initialization format" and "Mock object reference format" to use $MockField$

jchesshir commented 2 years ago

I had another thought on this one. Instead of handing it an index, I could instead hand it a parent interface name. Then the software could search for the first interface that is or inherits the parent interface and return the mock name for it, instead of imposing a standard on the user that the interface has to always be the <n>th interface found to mock.

Regarding your statement:

Seems fine with me. You'll probably also need to introduce a new format: "Mock field name" that defaults to mock$InterfaceMockName$, so you can actually get the mock field name cleanly. Then you could change the defaults for "Mock field declaration format", "Mock field initialization format" and "Mock object reference format" to use $MockField$

How would that be different from the the field "Mock object reference format"? I'm fine with making $MockField$ without parameters accessible from "Mock field declaration format" and "Mock field initialization format" and changing them to use in their defaults. That makes perfect sense. But it seems that "Mock object reference format" would be the source of that field, not another recipient of it.

While I'm at it, I might as well make $MockField$ accessible from both "Test method format" fields in addition to the "Main Content" field, even though I don't personally have a use for that.

I'm going to edit the description to have all three or four of these fields. You can tell me what you think.

jchesshir commented 2 years ago

Ok. Finished with edit. I left my original idea in there, although I'd be happy to remove it and just leave the latter two in there if you're good with that.

One more thought. I do not see this functionality to be of maximum benefit if issue #1 is not released before or with it. If the user cannot easily specify an alternate format for SUTs that do not include the expected interfaces, then the functionality will be more of a burden than a help. I recommend linking this ticket to it as a dependent.

RandomEngy commented 2 years ago

The $MockField$ addition is good, along with the $MockField<InterfaceOrClassName>$ variant.

I think we are on the same page but am not sure. The default values for "Mock field declaration format", "Mock field initialization format" and "Mock object reference format" should use the new $MockField$ token.

We should have a new customizable format: "Mock field name", defaulting to mock$InterfaceMockName$ or stub$InterfaceMockName$ depending on the framework.

By the way I renamed the section from "Interface content tokens" to "Mock dependency tokens".

jchesshir commented 2 years ago

I think we're on the same page now.

I think I now I see the difference between your new "Mock field name" field and the existing "Mock object reference format" field. It's the keyword "this." which can't be used in the "Mock field declaration format" field, although it could be used in the "Mock field initialization format" field. But that would require another field that is not also available for the "Mock object reference format" field and would be hard to document, so you'd rather just use $MockField$ for all three.

You also want the singular $MockField$ to be documented under what is now "Mock dependency tokens", since it will only be accessible to the three fields named in the description of that section.

Let me know if I've missed anything.

On Sat, Oct 16, 2021 at 12:36 AM David Rickard @.***> wrote:

The $MockField$ addition is good, along with the $MockField$ variant.

I think we are on the same page but am not sure. The default values for "Mock field declaration format", "Mock field initialization format" and "Mock object reference format" should use the new $MockField$ token.

We should have a new customizable format: "Mock field name", defaulting to mock$InterfaceMockName$ or stub$InterfaceMockName$ depending on the framework.

By the way I renamed the section from "Interface content tokens" to "Mock dependency tokens".

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/RandomEngy/UnitTestBoilerplateGenerator/issues/24#issuecomment-944860855, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACX6ORCXFJIBSQ42QLUSB5LUHEFMXANCNFSM5FYR356Q . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

RandomEngy commented 2 years ago

That's right. For example in Moq, the default mock object reference format is this.$MockField$.Object. So it needs to be separate.