How will another unit test framework cause me to write better tests than using MSTest?
You see you can have all the "nice features" in the world but if you still write unit tests against a system that isn't well designed or broken up in testable pieces, you will have a hard time writing maintainable unit tests. The other thing is that even if you have the most wonderful designed system and unit-test friendly classes to test against, you still sit with the problem of writing maintainable unit tests.
So I thought of rewriting the post and taking on this discussion from a different perspective. Instead of blabbering on about how this framework rocks and the other one doesn't let's ask the question above. Why against MSTest? Because it seems to be the underdog at the moment. Look at what some of the other bloggers have to say:
- Why would anyone use MSTest over NUnit? (See the main article and especially the long comment by Thommas Weller).
- Why I'm migrating from MSTest to XUnit.net?
- Why MSTest is the IE6 of unit test frameworks
- Firstly it seems to be another monolithic solution from Microsoft. Why not have a .NET testing solution which can be broken down in different testing modules so that you can use the best tool for the job? So then you can use the other testing tools from Microsoft but for Developer unit testing you use something like NUnit or something. But I know why this is the case because again Microsoft wants you to use their products. Now it wouldn't have been a problem if they have taken certain things into account like some of the bloggers up there have stated like extensibility, etc.
- Startup time is SLOW! And then it's only basic logic tests!
- There are plugins in Visual Studio to run NUnit tests that are free! (Not a MSTest gripe but many people stick to MSTest because its integrated. Speaking of which there are ways apparently, which I haven't tested myself, to run NUnit on a TFS build server. Check this link: "NUnit for Team Build").
Now you might say "I've done this and that with MSTest and it took me hours while I could have done the same in NUnit in minutes". Let's find out shall we?
I'm not going into UI tests and Web tests. Firstly I don't think something like NUnit should be brought anywhere near UI testing! MSTest might have a nice UI tester but you might need to purchase Visual Studio Ultimate or something to get hold of it. Neither am I going to do web tests which involve HTML and JavaScript! That's just a maintenance nightmare waiting to happen!
No I will solely stick to logic tests and maybe even integration tests. Let Developer unit testing BE developer unit testing, anything else, use some of the other tools that are more suited for the job!
Writing tests for small methods are fine because your tests are inherently small to test the small amount of work it needs to do. Stuff like math functions, you give it an input and it spits out an output, very simple and quick. You do the MATH! LOL!
Some of you may say "Well NUnit will simplify your life there, you can have 1 test method with a few value attributes". That's true so NUnit can give you some simplified and good test coverage there. MSTest will require the code duplicate approach or you have to write the plumbing (again it might not be what developers would like as MSTest is not as extensible as NUnit) to get to the same kind of ease-of-effort that NUnit guys are enjoying.
But let's make it more interresting!
How about writing tests against a WCF service where you need to stub out some calls to other services and have to validate that the correct logic is being followed?
So let's say I have a WCF service class:
public class TimeSheetService : ITimeSheetServiceContract
{
public PersonDetails GetPersonDetails(string id)
{
... some code here ...
}
}
Ok seems easy to test:
NUnit
[Test]
public void Get_user_details_from_timesheetservice_that_is_existing()
{
TimeSheetService service = new TimeSheetService();
var person = service.GetPersonDetails("12345");
Assert.That(person, Is.Not.Null);
Assert.That(person.Name, Is.EqualTo("James"));
}
MSTest
[TestMethod]
public void Get_user_details_from_timesheetservice_that_is_existing()
{
TimeSheetService service = new TimeSheetService();
var person = service.GetPersonDetails("12345");
Assert.IsNotNull(person);
Assert.AreEqual("James", person.Name);
}
Not much of a difference, right? But hang on a moment! Where do I stub the person with the name "James"?
Big deal, I'll just use my favorite mocking framework to do that.
I currently use Rhino Mocks as my mocking framework and I am open for discussion, so if you see me doing something wrong or you want to notify me on something else that Rhino Mocks users will like to move across to please let me know. Assume I have a preconfigured 'mock' object (will get to that later) and I have noticed that the service needs an IPersonRepository object.
NUnit
[Test]
public void Get_user_details_from_timesheetservice_that_is_existing()
{
IPersonRepository personRepository = mock.StrictMock<IPersonRepository>();
personRepository.Expect(x => x.GetPersonDetailsFromRepository("12345")).Return("James");
mock.ReplayAll();
TimeSheetService service = new TimeSheetService(personRepository);
var person = service.GetPersonDetails("12345");
Assert.That(person, Is.Not.Null);
Assert.That(person.Name, Is.EqualTo("James"));
}
MSTest
[TestMethod]
public void Get_user_details_from_timesheetservice_that_is_existing()
{
IPersonRepository personRepository = mock.StrictMock<IPersonRepository>();
personRepository.Expect(x => x.GetPersonDetailsFromRepository("12345")).Return("James");
mock.ReplayAll();
TimeSheetService service = new TimeSheetService(personRepository);
var person = service.GetPersonDetails("12345");
Assert.IsNotNull(person);
Assert.AreEqual("James", person.Name);
}
See how much it grew? Both are still the same length. The problem is that this is how we generally run into these things. If we don't take care, we usually end up with bloated unit test methods!
Now's the question is how can the unit testing framework make my life simpler?
Now let's forget using other libraries (AutoFixture as example) and let's forget writing helper methods (unless it's part of getting the framework to help deal with this). Let's get the unit testing framework to do most of the work for us. See what I mean with this? I know that there are stuff out there that can help deal with this but that's not the point. Can my unit testing framework help take care of this without me having to write helper methods/classes or rely on external solutions? If it cannot, then why? Those are the things I want to point out.
And so I present this as a question to you the reader. I know MSTest and I know NUnit but I don't know all the integral details that each offer to leverage it to my advantage. So if you look at the above and see some area where MSTest or NUnit can make my life easier please comment below. If you are experienced in another unit testing framework, please give your input as well! You don't necessarily have to give big code replies, just mentioning a feature and how it can be applied should help get a picture.
Just to finalize this let me give a broader picture of the problem and then hopefully people can start sharing their input so that we can really see how much of a difference a unit testing framework makes.
First there is the Rhino Mocks problem. How can I create a new MockRepository for each test and provide it in such a way that when I call the field (or property) "mock" that I have a ready to be used mock. Also don't forget that Rhino Mocks expect the "ReplayAll" to be called before you call your SUT (System Under Test) code and after the test is completed it need to "VerifyAll" against the mock repository object.
Secondly I want to test something else. I want to test the SubmitTime method.
Let me write out the code (only the relevant stuff) so that you can have a clearer picture of what needs to be tested:
public class TimeSheetService : ITimeSheetServiceContract
{
private readonly IPersonRepository _person;
private readonly ITimeCaptureRepository _timeCapture;
public TimeSheetService(IPersonRepository person, ITimeCaptureRepository timeCapture)
{
_person = person;
_timeCapture = timeCapture;
}
public PersonDetails GetPersonDetails(string id)
{
var result = _person.GetPersonDetailsFromRepository(id);
return new PersonDetails { Name = result.Name, Categories = result.Categories };
}
public bool SubmitTime(TimeSubmitRequest request)
{
PersonDetails personDetails = GetPersonDetails(request.PersonId);
bool added = _timeCapture.SubmitTimeCapture(request.PersonId, personDetails.Categories, request.TimeSlots);
return added;
}
}
Now the Id or PersonId is a string, Categories are IList<string> and TimeSlots are IList<TimeSlot> and TimeSlot is of class of properties { DateTime StartTime, int Minutes, string Category }
If there are somethings that are not clear let me know.