There’s actually news in the world of SpecsFor! .NET Standard support has arrived, plans for the next version are becoming a little more concrete… read on if you want to catch up!
The Journey to .NET Standard
“One can only slack for so long…” – Me, when I finally started work on .NET Standard support for SpecsFor
I am probably one of the worst open-source project maintainers, ever. I’ve let SpecsFor languish without .NET Standard support for far too long.
I had my reasons though. Prior to 2018, most of my production work still targeted .NET 4.7. To be perfectly honest, I didn’t even like .NET Core until 2.1. I feel like we’re just now hitting the point where it’s an actual, viable, stable solution. I’m only just now diving in to the pool all the way.
So there was that lack of motivation, but then there were also the dependencies!
SpecsFor is actually built on top of several different libraries: NUnit
, Jeremy Miller’s StructureMap
, kzu’s Moq
, Derek Greer’s ExpectedObjects
, and Eric Hexter’s Should
. SpecsFor has varying degrees of dependencies on each of these libraries. You might (correctly) say it is tightly coupled to each of these projects.
This made perfect sense to me at the time. SpecsFor was originally meant to be a highly opinionated framework: you wrote specs using a specific combination of tools, in a specific way, with some glue in the middle to help make things work together nicely.
That’s an awesome idea, and it works fine… until one of those dependencies stops being maintained. That is exactly what happened to Should
. In order for SpecsFor to target .NET Standard, I would have to break that dependency on Should
.
That turned out to be more difficult than you might think, and in the end, I actually ended up forking Should
, reworking that for .NET Standard, and shipping that as its own NuGet package, Should-DotNetStandard
.
This is a temporary solution for the 6.0 release. I don’t plan to support Should-DotNetStandard
for long. In fact, I don’t plan for SpecsFor to remain coupled to much of anything in the future. But we’ll get to that in a second! Let’s talk about ASP.NET MVC.
Saying Goodbye to SpecsFor.Mvc
As much as it pains me, I’m planning to retire SpecsFor.Mvc. I’ll continue to fix bugs and accept pull requests for the foreseeable future, but it will not be getting ASP.NET Core support. If you want to use it for ASP.NET MVC 5, feel free to do so! But don’t expect to see it working with ASP.NET Core.
I have my reasons for retiring this project. It was always difficult to maintain and support, but more than that, I just don’t feel that there’s much of a fit for it in ASP.NET Core. Microsoft decided to basically abandon the whole strongly-typed approach to things when they deprecated HtmlHelpers
and went with TagHelpers
in ASP.NET Core. Beyond even that, there’s the fact that most apps, at least the ones I work with, are built on front-end frameworks like Angular, and those frameworks have their own tools for UI testing.
So that’s that: SpecsFor.Mvc is still out there and will still work for ASP.NET MVC 5 apps. I’ll still try to fix bugs as they are found, and I’m still accepting pull requests. In fact, I’m shipping a small update today thanks to a pull request from Nick Divinci, plus a few minor improvements to the code itself.
But, unless someone wants to step in and champion the work (or pay me to champion it?), it won’t be following its sibling into the land of .NET Standard.
With that depressing bit out of the way, let’s talk about what’s next for SpecsFor!
SpecsFor vNext
My focus with the SpecsFor 6.0 release is to get things working stably as a .NET Standard package. I don’t plan to add new features to this version. I just want to get it all working, and working well, on .NET Core, .NET Full, etc.
I have very different, probably overly-ambitious, goals for SpecsFor 7.0.
For SpecsFor 7.0, I want to accomplish two contradictory goals at once: provide a solution that is flexible and can be used with the tools of your choice, and provide a highly-opinionated solution with the latest-and-greatest tooling. How can I do both of these things at once? Here’s what I’m thinking:
- SpecsFor.Core – The core “engine” of SpecsFor. No dependency on anything else, just some abstractions.
- SpecsFor.Assertions – Framework-agnostic test helpers. Things like
ShouldLookLike
and other common helpers would live in this package. - SpecsFor.Web – Framework-agnostic helpers to simplify common ASP.NET Core testing scenarios.
- SpecsFor.EF – Framework-agnostic helpers to simplify common Entity Framework testing scenarios, enabling simple, easy integration testing.
Those will be the “core” packages, with as few dependencies as I can get away with in each. The idea is that you can pick and choose what you need, configure it to use whatever testing framework, mocking framework, and assertion library that you want. I hope to have packages available for common configurations, probably something like:
- SpecsFor.NUnit – Wiring to enable SpecsFor to work on top of NUnit.
- SpecsFor.XUnit – Wiring to enable SpecsFor to work on top of XUnit.
- SpecsFor.StructureMap – Configures SpecsFor to use an auto-mocking StructureMap container for creating everything (just like it does today).
- SpecsFor.SimpleInjector – Configures SpecsFor to use an auto-mocking SimpleInjector container.
- SpecsFor.Moq – Configures SpecsFor to use Moq for creating mocks.
- SpecsFor.FakeItEasy – Configures SpecsFor to use FakeItEasy for creating mocks.
You get the idea: small, modular packages, that you can mix and match to fit however you want to write your specs.
But I still plan to have a “mega” package:
- SpecsFor – The highly-opinionated kitchen sink. Built on SpecsFor.Core, wired up to use a specific testing framework, a specific set of assertions, a specific mocking library, etc.
I’m not sure exactly which tools I’ll choose for this mega package yet. I know I want to use Shouldly
for its awesome exception messages. Other than that, I may end up keeping the current set of tooling. I’m also tempted by XUnit, but its approach to testing is not really compatible with SpecsFor, and I’m still not sure how I’m going to make SpecsFor work on top of it. If you think I should ditch one tool for another, let me now.
It’s going to be a lot of work, but I think that will greatly open up the places that SpecsFor can be applied. Plus it’ll be fun (I think)!
Interested in Helping?
All of that said, 7.0 is going to be a lot of work. I’d love to get some help on this. If you’re interested in championing any one of these packages, or helping with the core redesign, please contact me! And if you work for a company that is using (or interested in using) SpecsFor, and you’d like to sponsor some of the new work, let me know! ?
As we’ve discussed – xUnit is really just designed very differently. I mean, I think you can have it run with SpecsFor fine, but it’s going to run the Given/When each test run. It’ll be slow as hell for integration tests (probably fine for unit tests). I think it’s a fool’s errand to try to get it to behave how nUnit does (or I’m a fool for not seeing some obvious solution).
As far as DI containers – I believe AutoFac to be more popular than SimpleInjector. Note that I don’t mean that to say one is better than the other – just more popular. 30 seconds of googling shows SI as faster than AF. Only reason I honestly bring it up is because we want to move to SpecsFor, but existing code is already using AutoFac. https://dotnet.libhunt.com/compare-autofac-vs-simpleinjector
Brief discussions on my team mentioned that probably SpecsFor could be made to use the MS DI stuff underneath, allowing you to then sub in your own framework as each package. I’m still a newb to how all that works though, and can’t even make words properly about it yet.
I’m sorry to hear the SpecsFor.MVC is going away. I haven’t done any .Net standard MVC projects yet, so the difference isn’t quite “real” to me yet, but I’ll take your word for it that they’re simply not the same animal anymore. I was also sad to see Should fade away. I contributed to that thing. Anything with “LessThan” or “GreaterThan” was mine. “Shouldly” is a decent almost-drop-in replacement for Should, and not quite as abandoned. You might consider making that part of the landscape. I’m totally in the StructureMap camp as far as IoC goes. I’ve watched others such as Ninject come along with some fancy new trick, only to see the “oh yeah… me too” in the next version of StructureMap. I’ve settled on it as the stable, reliable option, and lets face it, if your IoC has become your bottleneck, then you are pretty much a god, and ready to write your own anyway. For testing, I prefer NUnit because MSTest is nice, but I never really felt that their heart was in it. It’s more like something they had to write rather than something they wanted to write. Still, it comes up, and some clients demand it, so the flexibility will be nice.
I’m kinda bummed about SpecsFor.Mvc, too. It’s awesome watching a suite of specs execute at full speed, testing the UI…
As far as test frameworks, it seems like XUnit is the chosen one right now. Like @disqus_e59C4JWvT5:disqus mentioned, the way it executes specs doesn’t really fit with how SpecsFor works, but I have ideas on how to bend it to my will! 🙂
Hi Matt – I stumbled across SpecsFor.Mvc in my new company and we will certainly be continuing to use it for some time 🙂 so I can help with any fixes while it is in legacy.
But would love to keep an eye and contribute on the v7.0 version!
Speak soon!
Awesome! So you guys are using ASP.NET MVC 5?
I was curious whether you had any plans to support MSTest. I know it gets made fun of a lot, but I think it hit parity with NUnit a while back, and as long as you’re using an external assertion library like Should(ly), they should be almost identical. The main upshot is that MSTest doesn’t require any plug-ins to visual studio to work. If there’s something structural that’s different about the two that makes it impossible, then I’d be interested to know. I’ve always been on the NUnit side of things, but I run into the pain of getting the runner to see the tests every time I start a new project… “Oh yeah… you had to have an add-in for those to work… what was that thing called again?” Maybe the SpecsFor.NUnit package could just go ahead and take a dependency on NUnit3TestAdapter and get it all over with in one shot.
I hope to support MSTest with the new architecture. If nothing else, I think bundling the NUnit3TestAdapter with SpecsFor makes perfect sense. That’s probably something that could happen with v6. I’ve always used either TestDriven.NET or ReSharper as my test runner, so I’m pretty blind to the pain of the built-in VS test runner. 🙂