If You Write An Article About TDD, Make Sure It Is Correct
I spend a majority of my free time reading articles and blog posts, having discussions on Twitter or engaging in conversations on conferences and community events. I'm realistic enough to understand that my opinions are not necessarily the truth, so I use those media to challenge my own ideas and learn about solutions I would never think of myself.
I recently read an article titled Test-Driven ASP.NET MVC
that, although I respect what the author is trying to convey, implies
some things I strongly believe are incorrect or are malpractices.
Normally I wouldn't bother to blog about this, but because his article
is published at the online MSDN Magazine, I couldn’t resist the urge to correct some things.
- Thoroughly understand the MVC pattern. As Martin Fowler has clearly explained in his post on GUI patterns,
the view doesn't manage the presentation of models, nor does it handle
interactions with the users. Both are the responsibility of the
controllers. They react to keyboard, mouse (and touch) input, use that
information to interact with the (domain) model and then decide what
view should be used to render the result. In that sense, ASP.NET MVC is
reasonably faitful to the original pattern. The only big difference is
that in the original MVC pattern, the controllers were directly handling
user input whereas in the ASP.NET MVC the controllers merely handle
HTTP POSTs, GETs and PUTs.
- Tiers are not layers. Tiers are used to represent a physical separation whereas layers represent a logical separation. You can have all layers (e.g. presentation, service, domain and data access) hosted on a single tier if you want, or if you can assign them to multiple tiers. An example of that is an ASP.NET web site (the presentation layer) hosted on a front-end web server, the service, domain and data access layers hosted on a Intranet application server, and the database hosted on a dedicated database server. The other way around doesn't work like that though. If you didn't account for layering in your architecture, it might be very difficult to deploy your system in a distributed environment.
- Keep your Visual Studio projects to a minimum. Having many projects such as the project-per-layer in the article considerably slows down compiling, but also causes the startup time of the corresponding application to increase. Loading 10 small assemblies is much slower than loading one big assembly. Jeremy D. Miller mentioned this already in this post, but Patrick Smacchia, the author of NDepend, provides some good proof of if it here. Read my original post for more guidance on splitting up projects.
- Separate test code from production code. The author recommends separating test code from production code, a good thing obviously. In fact, I would not even dare to think of polluting the production code base with test code. It might double the size of the assemblies and considerably increase the memory footprint of the application. But as usual, it is not always possible to avoid this. We sometimes have to make a method internal and allow the test projects to access those members using an [InternalsVisibleTo] assembly-level attribute. But that's an exception. What we do more often is make certain methods protected so that we can apply the Test-Specific Subclass pattern.
- TDD is about test-first development.
The article refers to the Test-Driven Development practice a few times
but never explains its essential concepts such as writing tests before
writing the production code, the red-green-refactor mantra and the many
benefits it offers from a design and maintenance perspective. In fact,
not a single code exampl shows what a unit test would look like when
practicing TDD during the development of an ASP.NET MVC. Why then call
the article Test-Driven ASP.NET MVC. This is even more surprising that
author is using Jeremy D. Miller's
StructureMap, who has been one of the most noticeable TDD advocates in
the community. I wonder whether the author really understands TDD at
all.
- Use Fakes with caution. This is also amplified by his apparent nonchalant use of Visual Studio 2012's support
for generating fakes for his production code. If you're practicing TDD,
you explicitly design the interface of your API or framework, as well
as its dependencies. IMHO, this is an essential part of the thought
process involved in TDD. Using a brute-force tool like Fakes or TypeMock
Isolator allows you to ignore that part of the process and just
generate whatever is needed for your test. I'm not saying Fakes or
TypeMock are bad products, but because of their unique capabilities
compared to popular mocking frameworks like Moq, Nsubstitue or my own
favorite, FakeItEasy, I'm afraid many people will follow the author's example.
- Dependency injection is not the same as the inversion of control principle.
I hate it when developers use the terms Dependency Injection and
Inversion-of-Control interchangeably. They are two different things and
if you don't understand that, make sure you thoroughly read the chapter
on the Dependency Inversion Principle in Pablo's SOLID guidelines.
Inversion-of-control is essentially about removing the direct
dependencies of one class to another out of that class and providing an
abstraction that that class relies on. The inversion in this principle
is about moving the responsibility for hooking up the original class
with a concrete implementation of that abstraction to a 3rd party,
typically an IoC framework. This will improve your chances for loose
coupling, better testability, and increased maintainability on the long
run. Obviously you don't have to use a DI framework, but if you do it
correctly you can gain a lot of benefits from it. I myself heavily rely
on Autofac after having used Microsoft Unity for a few years.
And remember, just like any principle or tool, use it with caution.
Overusing IoC can result in systems were it is difficult to understand
what happens.
- Resolving dependencies from inside a class completely defies the ideas behind DI. Why in the world would you suggest people to use an DI framework like StructureMap and then use a static class to resolve dependencies from within the dependent class? That vaporizes the entire purpose of a DI framework. It's not for nothing that Jeremy D. Miller wrote a post on Push, Don't Pull. Even better, that's why the word injection is in Dependency Injection! That is also why the author suggest registering a fake object in his IoC container. This is clearly a design smell resulting from his apparent incorrect understanding of the IoC principle.
- One more issue.
In the discussion on namespaces and test code, the author suggested to
use the name Test (singular) in the project name. Since the namespaces
in projects should use the project name as their base, you end-up with
the name of something that could be the name of a class. That has been a
violation of the Microsoft Design Guidelines and FxCop naming rules
since the inception of the .NET Framework. I usually use the name Tests,
Testing or Specs for the project that holds the unit and integration
tests.
Anyway, the morale of this post is that you should get your facts straight before you write an article that is going to reach so many people. And now that I think of it, why didn't somebody from the MSDN Magazine's staff check this article in the first place? I might be wrong as well, so if disagree let me know by commenting on this post or by pinging me through my twitter handle at ddoomen.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)
- methodology
- opinion
- TDD
- test
- Tools