toddams / RazorLight

Template engine based on Microsoft's Razor parsing engine for .NET Core
Apache License 2.0
1.51k stars 260 forks source link

Added regression tests for common Razorlight renderer cases #309

Closed weyert closed 4 years ago

weyert commented 4 years ago

I have created a few tests which cover common cases which I frequently use in my projects that leverage Razorlight project which include:

Also I added a new dependency for the Tests project which is Snapper to testing the rendered results easier by using snapshot testing.

weyert commented 4 years ago

This gives the following results in Rider:

RazorLight.Tests.IntegrationTests.RendererCommonCasesTests.Should_Fail_When_Required_Section_Is_Missing

RazorLight.Compilation.TemplateCompilationException : Failed to compile generated Razor template:
- (6:6) The name 'section' does not exist in the current context

See CompilationErrors for detailed information

   at RazorLight.Compilation.RoslynCompilationService.CompileAndEmit(IGeneratedRazorTemplate razorTemplate) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RoslynCompilationService.cs:line 140
   at RazorLight.Compilation.RazorTemplateCompiler.CompileAndEmit(RazorLightProjectItem projectItem) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RazorTemplateCompiler.cs:line 201
   at RazorLight.Compilation.RazorTemplateCompiler.OnCacheMissAsync(String templateKey) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RazorTemplateCompiler.cs:line 156
--- End of stack trace from previous location where exception was thrown ---
   at RazorLight.EngineHandler.CompileTemplateAsync(String key) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/EngineHandler.cs:line 51
   at RazorLight.EngineHandler.CompileRenderAsync[T](String key, T model, ExpandoObject viewBag) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/EngineHandler.cs:line 131
   at RazorLight.Tests.IntegrationTests.RendererCommonCasesTests.Should_Fail_When_Required_Section_Is_Missing() in /Users/weyertdeboer/Development/Third-Parties/RazorLight/tests/RazorLight.Tests/IntegrationTests/RendererCommonCasesTests.cs:line 61
--- End of stack trace from previous location where exception was thrown ---

RazorLight.Tests.IntegrationTests.RendererCommonCasesTests.Should_Render_RequiredSections_That_Have_RenderAsync

RazorLight.Compilation.TemplateCompilationException : Failed to compile generated Razor template:
- (6:6) The name 'section' does not exist in the current context

See CompilationErrors for detailed information

   at RazorLight.Compilation.RoslynCompilationService.CompileAndEmit(IGeneratedRazorTemplate razorTemplate) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RoslynCompilationService.cs:line 140
   at RazorLight.Compilation.RazorTemplateCompiler.CompileAndEmit(RazorLightProjectItem projectItem) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RazorTemplateCompiler.cs:line 201
   at RazorLight.Compilation.RazorTemplateCompiler.OnCacheMissAsync(String templateKey) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RazorTemplateCompiler.cs:line 156
--- End of stack trace from previous location where exception was thrown ---
   at RazorLight.EngineHandler.CompileTemplateAsync(String key) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/EngineHandler.cs:line 51
   at RazorLight.EngineHandler.CompileRenderAsync[T](String key, T model, ExpandoObject viewBag) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/EngineHandler.cs:line 131
   at RazorLight.Tests.IntegrationTests.RendererCommonCasesTests.Should_Render_RequiredSections_That_Have_RenderAsync() in /Users/weyertdeboer/Development/Third-Parties/RazorLight/tests/RazorLight.Tests/IntegrationTests/RendererCommonCasesTests.cs:line 76
--- End of stack trace from previous location where exception was thrown ---

RazorLight.Tests.IntegrationTests.RendererCommonCasesTests.Should_Render_Section_And_ViewModel

RazorLight.Compilation.TemplateCompilationException : Failed to compile generated Razor template:
- (6:6) The name 'section' does not exist in the current context

See CompilationErrors for detailed information

   at RazorLight.Compilation.RoslynCompilationService.CompileAndEmit(IGeneratedRazorTemplate razorTemplate) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RoslynCompilationService.cs:line 140
   at RazorLight.Compilation.RazorTemplateCompiler.CompileAndEmit(RazorLightProjectItem projectItem) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RazorTemplateCompiler.cs:line 201
   at RazorLight.Compilation.RazorTemplateCompiler.OnCacheMissAsync(String templateKey) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RazorTemplateCompiler.cs:line 156
--- End of stack trace from previous location where exception was thrown ---
   at RazorLight.EngineHandler.CompileTemplateAsync(String key) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/EngineHandler.cs:line 51
   at RazorLight.EngineHandler.CompileRenderAsync[T](String key, T model, ExpandoObject viewBag) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/EngineHandler.cs:line 131
   at RazorLight.Tests.IntegrationTests.RendererCommonCasesTests.Should_Render_Section_And_ViewModel() in /Users/weyertdeboer/Development/Third-Parties/RazorLight/tests/RazorLight.Tests/IntegrationTests/RendererCommonCasesTests.cs:line 31
--- End of stack trace from previous location where exception was thrown ---

RazorLight.Tests.IntegrationTests.RendererCommonCasesTests.Should_Render_Sections_With_IncludeAsync

RazorLight.Compilation.TemplateCompilationException : Failed to compile generated Razor template:
- (6:6) The name 'section' does not exist in the current context
- (10:6) The name 'section' does not exist in the current context

See CompilationErrors for detailed information

   at RazorLight.Compilation.RoslynCompilationService.CompileAndEmit(IGeneratedRazorTemplate razorTemplate) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RoslynCompilationService.cs:line 140
   at RazorLight.Compilation.RazorTemplateCompiler.CompileAndEmit(RazorLightProjectItem projectItem) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RazorTemplateCompiler.cs:line 201
   at RazorLight.Compilation.RazorTemplateCompiler.OnCacheMissAsync(String templateKey) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/Compilation/RazorTemplateCompiler.cs:line 156
--- End of stack trace from previous location where exception was thrown ---
   at RazorLight.EngineHandler.CompileTemplateAsync(String key) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/EngineHandler.cs:line 51
   at RazorLight.EngineHandler.CompileRenderAsync[T](String key, T model, ExpandoObject viewBag) in /Users/weyertdeboer/Development/Third-Parties/RazorLight/src/RazorLight/EngineHandler.cs:line 131
   at RazorLight.Tests.IntegrationTests.RendererCommonCasesTests.Should_Render_Sections_With_IncludeAsync() in /Users/weyertdeboer/Development/Third-Parties/RazorLight/tests/RazorLight.Tests/IntegrationTests/RendererCommonCasesTests.cs:line 46
--- End of stack trace from previous location where exception was thrown ---
weyert commented 4 years ago

@jzabroski Can you please have a look if my templates make sense? Maybe I am using Razorlight totally wrong :angel:

jzabroski commented 4 years ago

I'm not clear on what Snapper is, and the nuget.org page says to see the Project for details. I then went to that Project page, https://theramis.github.io/Snapper/ , and it links back to the Nuget page and briefly says, "captures snapshots of objects to simplify testing. It is very heavily based on Jest Snapshot Testing."

I don't know what a snapshot of an object is, even though I can guess.

I think Snapper's documentation is really bad or the style of code it encourages is really bad - I can't figure out what it's doing, so I'm not sure which. The GitHub page frankly confirms this:

After a lot of thought I've decided to deprecate Snapper V1. Snapper V1 was my first attempt at an OSS library and some of the decisions I made very early on made it very difficult to add new features. Snapper V2 is my second attempt at making the library easier to use and update.

I hope the author figures out how to document this library. Maybe it's useful. But it is definitely not ready for massive adoption.

Any time I have to go read the documentation of another framework, jestjs, to figure out what something is trying to do, I get a little worried... (The irony of RazorLight depending on Razor language documentation is not lost on me.)

Basically, according to jestjs, it looks like they save a file in your test directory that records a snapshot... gross. How is that going to work with CI/CD servers where you blow away the agent after you're done? This Snapper project just seems so half-finished to me.

The other piece of documentation that concerns me is this talk of "child snapshots"...

The last thing about Snapper is it doesn't allow randomizing your test inputs, e.g. with AutoFixture. So your tests need to be brittle for this to work.

weyert commented 4 years ago

@jzabroski I am happy to use something else; what would you advise to test whether generated template output is as expected?

The idea of snapshots is that you write the expected result to a file which you commit to the git repo and only update them when the input has changed so the snapshot of the results need to be updated. You do this by adding the [UpdateSnapshots]-attribute temporary to your method or class and remove it after you finish working on the unit tests.

See: https://killalldefects.com/2019/08/30/snapshot-testing-in-javascript-net/

jzabroski commented 4 years ago

The idea of snapshots is that you write the expected result to a file which you commit to the git repo and only update them when the input has changed so the snapshot of the results need to be updated. You do this by adding the [UpdateSnapshots]-attribute temporary to your method or class and remove it after you finish working on the unit tests.

See: https://killalldefects.com/2019/08/30/snapshot-testing-in-javascript-net/

Got it.

I'm not stubborn and always willing to learn new tricks, I just spent 30 minutes trying to figure out how to use it and found no description of that basic workflow you just described. Maybe you could consider helping with their documentation, too? :)

Basically, this is "gold copy testing" with a new buzzword attached to it, where the unit testing framework generates the gold copy via this [UpdateSnapshots] attribute.

One last question - how does the csproj know the gold copy exists? I'm a little lost on that point. Because for it to do a proper check at runtime during testing, it would have to know that file got copied over to the output directory.

I think these are the types of things the Snapper project should add to its landing page.

I'm now leaning towards the idea this is probably a good idea to use Snapper - @toddams Thoughts?

toddams commented 4 years ago

I haven't had a chance to work with it either. But I'm reading an explanation and it sound pretty nice. Nonetheless, 36 stars library on Github with bad documentation brings a little concern. Maybe there is an alternative library that uses similar approach, but is more popular/better documented?

weyert commented 4 years ago

I am not aware of any other library for this, at work we used our own creation until this library was discovered.

jzabroski commented 4 years ago

@weyert I am going to try to get Snapper integrated this week.

robdmoore commented 4 years ago

Taking a look around this library and stumbled on this PR. I've got extensive experience with "snapshot testing" (I've always known it as approval tests) so thought I'd comment in case it was helpful.

Good tools for snapshot testing in .NET are approval tests and shouldly's .ShouldMatchApproved(). I've been using both for years and they work really effectively.

I actually like these approaches better because they pop up a diff window when there is a change and you can inspect it and approve it using the diff tool rather than having to modify the code with an [UpdateSnapshot] attribute.

Totally agree that testing approach is good for this kind of test though šŸ‘

jzabroski commented 4 years ago

@robdmoore Thank you. I think I had looked at shoudly a very long time ago (2012?) and didn't find it interesting, but your comment has reignited a spark of interest. Is there a YouTube video I could watch to understand the workflow you're describing?

jzabroski commented 4 years ago

@robdmoore Also, are you suggesting we use ApprovalTests.Net, shouldly, or both? Would help cut down the learning curve if you could direct me on what to focus on learning. The whole pop open a diff window and confirm thing completely sold me, though. I can definitely see that as a valuable tool to add to my arsenal.

robdmoore commented 4 years ago

You'd use one or the other, but are equivalent type of functionality.

Shouldly is an incredible library for assertions in general. The clever stuff it does with assertion messages is top class. e.g. this example from their readme.md:

This is the old Assert way:

Assert.That(contestant.Points, Is.EqualTo(1337));

For your troubles, you get this message, when it fails:

Expected 1337 but was 0

How it Should be:

contestant.Points.ShouldBe(1337);

Which is just syntax, so far, but check out the message when it fails:

contestant.Points should be 1337 but was 0

It means it's much quicker to diagnose errors.

In terms of approvals, approval tests is configured using attributes and Shouldly has a nice fluent syntax at the point of making the assertion. Which one you like comes down to personal preference I guess!

In terms of getting started:

@jakeginnivan / @josephwoodward - anything to add?

jzabroski commented 4 years ago

Thanks, I found Simon Cropp's latest project, DiffEngine, today. It appears DiffEngine is one componet of Verify, a similar tool. He mentions ApprovalTests.Net in his Verify Alternatives.

I have to say, though, Verify/Shoudly seem to be the two nicest libraries.

SimonCropp commented 4 years ago

i added a PR based on this that shows how to use Verify https://github.com/toddams/RazorLight/pull/332

robdmoore commented 4 years ago

LYW @SimonCropp!

jzabroski commented 4 years ago

@robdmoore what does LYW stand for? Love Yob's Wumpus ?

robdmoore commented 4 years ago

Haha, wow - that brings back memories from my AI unit in uni where we coded a wumpus game!

No, LYW = Love Your Work :)

--

On 13 Apr 2020, at 8:03 pm, John Zabroski notifications@github.com wrote:

ļ»æ @robdmoore what does LYW stand for? Love Yob's Wumpus ?

ā€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

jzabroski commented 4 years ago

As I mentioned to @SimonCropp, Yob's Wumpus may be the perfect metaphor for his open source contributions. From Wikipedia's description

In the game, the player moves through a series of connected caves, arranged in aĀ dodecahedron, as they hunt a monster named the Wumpus. TheĀ turn-basedĀ game has the player trying to avoid fatalĀ bottomless pitsĀ and "super bats" that will move them around the cave system; the goal is to fire one of their "crooked arrows" through the caves to kill the Wumpus.

Yob's Wumpus is a perfect metaphor for how @SimonCropp is moving across GitHub submitting PRs.

weyert commented 4 years ago

I am fine with any solution; the one I used is something that closely resembled what I used on the front-end site hence this choice.

jzabroski commented 4 years ago

I'm going to merge this soon. My original plan was to merge @SimonCropp 's PR but he had canceled it before I could merge it. I would like to move to his approval testing framework as opposed to Snapper because Simon is super active/responsive maintainer and his projects are built out of composable, independent pieces. Snapper, by contrast, is not as well documented and has some limitations. The FAQ of Verify and ApprovalTests.Net cover what Snapper and other frameworks miss.

As some back context, I also run FluentMigrator project and I've been pushing hard to refactor the code base to work flawlessly with .NET Core 3.1. That has been my main focus and a lot of work. This past week I cleared out a backlog of PRs for FluentMigrator for 3.2.7 release, and made some progress on 3.3.0. I mention this because my plan is to focus on RazorLight once FluentMigrator has proper .NET Core 3.1 support, because it will make future releases much easier/labor intensive.

jzabroski commented 4 years ago

@weyert , I merged Simon's commit instead but I have drafted the release notes and given you credit for this PR. Thanks for getting this rolling. Are you still using C# at your new job?