Try our conversational search powered by Generative AI!

Henrik Fransas
Jan 17, 2015
  6436
(1 votes)

Doing unit testing or not not doing unit testing

I recently tried to help in a thread about unit testing and EPiServer and it ended up in a little discussion on how to and what to do unit testing on.

I am no fan of TDD and do not like having a lot of unit test in a project. That does not mean that I do not think there needs to be any test in a solution, what I mean is that it should be test for those things that are very important and not for those things that “should work” like for example getting a page from the CMS. The IContentRepository.Get<>() should EPiServer be responsible for to have tested before releasing the software.

I also having problem with mocking away to much, I have seen test where everything has been mocked away so the only thing that was tested was the mocking, so the test did not do any good at all. I like test that test the whole thing and that is not a unit test, but a integration test or maybe a behaviour test.  When I use unit test is mostly when building up new functions or solutions where I want to write a lot of logic that depends on for example a response from EPiServer Find and I do not want to wait to fire up the whole site and then typing in a query, do the request and then use the response. Those times I mock away EPiServer Find until I am almost done, then I start to use the “real” thing.

I am hoping this blog post can be a starting point for a login discussion on when to do unit tests and when to not do it and I am working on some examples where I thing it is good to have them that I will show here.

So… What do you all great developers out there think? Please help me make this a interesting and good blog post Ler

Jan 17, 2015

Comments

Adam Finzel
Adam Finzel Jan 18, 2015 03:45 PM

You should be testing the code you write. This ensures that the code you write does what you want it to do. It also means if someone else works on your code, that your code still does what you want it to do (unless of course, they just change your tests but thats what code review is for). Unit tests should also allow you to refactor your code and still be sure that it works the way you expected it to when you wrote it.

You are correct that you shouldn't be testing Episerver methods, they should be mocked away and when you call them you should be testing that you are calling them with the expected parameters.

Henrik Fransas
Henrik Fransas Jan 18, 2015 09:03 PM

First and important. I am doing testing in my projects but more integration test than unit test.
I also thinks that sometimes it gets more expensive to have a lot of test that has to be updated and changed. This is true when living in a "web world", I you for example write a calculator it is good to have a lot of unit test to make shore that it does the right thing and the specifications rarely change, 1 + 1 should always be 2. In the world I usually works, the specifications change all the time and it should do that because the customer change it's mind or need. If I then have 200 test that has to be changed because the customer decide that it should not be any date in the footer of the page, it is going to be a expensive change.

I think unit tests are good, integration tests are good but they should be used where they are really needed, not to much and not to little.

Arve Systad
Arve Systad Jan 19, 2015 09:30 AM

http://www.amazon.com/The-Art-Unit-Testing-Examples/dp/1933988274 - The Art of Unit Testing by Roy Osherove gives a good introduction to central concepts. Both how, why and what.

And you should not be testing EPiServer's APIs. That's EPiServer's job, basically - not your. You should mock the EPiServer responses and then test your own code.

K Khan
K Khan Jan 19, 2015 10:15 AM

In a practical world,
Units testing works well if project architecture is strictly adhere to SOLID principals, otherwise can prove a nightmare.
Unit testing can fail when it is just another piece of code and has been written to justify that we do Unit Testing.

Jan 19, 2015 01:49 PM

A big benefit of TDD in my opinion is that it forces me to think about the code I'm going to write before I write it. Code written after tests are (for me at least) usually a lot more thought through and clean than if I do it the other way around.

Henrik Fransas
Henrik Fransas Jan 19, 2015 02:52 PM

Arve. I have actually been on a two day custom course with Roy and that is the only course I have ever leaved before it was done, I was on it for one day and day two I was working because I did not give more than that I was irritated on his thinking. He was so extreme in his thoughts that is was a joke, one of the things that made me not attend day two was that he did not allowed me to use .Sum() on a IList, instead I was supposed to do a foreach loop because I could not be shore that the Sum() function was written with good code. Actually he hated all about lamda expression and he thought his thinking on what was good code was more important than learning TDD. Now that I have learned more, I can understand why you should write silly tests when learning but that time, with a lot of things to do in the ordinary work, I had a hart time to write one test for testing for null and one for testing for empty string and so on,...

Daniel. That is true and I use it a lot when doing integration solutions and so, but have a hard time finding times when it is better to do a unit test versus a integration test in a web site.

What I would like is that when creating a alloy solution from the visual studio addon it would also create a test project with some good examples of test where the EPiServer parts are mocked away and so. Daniel, a little feature request :)

Arve Systad
Arve Systad Jan 19, 2015 09:33 PM

Remember though, that the real benefits of unit testing is not speed of development when you're sitting there on a brand new project that takes seconds to fire up and click around in. That's always going to be fast. It's for making sure stuff is not broken later on as things change (and things *do* change, as we know). That's why I don't think you can say "it is better" to test in the browser all the time. Because you really won't know the answer to that until a few months of development and maintenance has passed by.

If Roy actually insisted on not using Sum(), that sounds kindof weird to me too - I must admit. Sure he wasn't using it as a harsh example, since it was a course after all?

Anyways; If you can test a lot of your solution with unit tests, you can be sure that things still work in the future. Especially things like calculations or important data transformations should be tested. And also, as Daniel Tempel says - it forces you into writing cleaner code. Smaller methods. Logically separated classes and routines. It forces you to not make a mess. If everything you do can be separated into small, simple methods - they are usually also testable. And if you can test every tiny bit like that, you can isolate those parts out of your debugging totally when an error occurs in production a late friday evening :-)

That said, since we mostly are dealing with EPiServer-interfaces, things can become complicated to mock (for instance, mocking parts of a page tree to test traversion and looking for specific things is a chore) - so there's a fine balance in what to test and what not to.

However, all integrations you do with other systems can usually be wrapped in your own interfaces and facade-classes, making them very testable. And in those cases, you should absolutely try to do so. It is not without reason that unit testing as a concept has been an industry standard for many years.

I know the Episerver-community has been slow to catch up on this (I began working with EPiServer four years ago, and testing didn't really seem to be a recognized concept back then), but with the "Episerver 7 era", things are really changing to the better.

Henrik Fransas
Henrik Fransas Jan 20, 2015 07:32 AM

I totally agree with you Arve and thanks for making this blog post be that discussion I was hoping for. I use it a lot when building integration solution and like but have a hard time doing it in a standard EPiServer web project. It might be because it is quite hard to do with EPiServer and there are not so many good examples on how to mock away EPiServer to be able to test fully. Erik did show an example of something that should be quite simple to test but is not in this thread: http://world.episerver.com/Modules/Forum/Pages/Thread.aspx?id=114885
He had a function in one of his pagetypes that looked like this:
public string GetFooterText()
{
return PageName + " published at " + StartPublish;
}
And when trying to do a test like this:
var page = new MyPage();
page.PageName = "a";
page.StartPublish = DateTime(2015,1,2);

Assert.That(page.GetFooterText(), Is.EqualTo("a published at 2015-01-02");

He got an error saying:
EPiServer.Core.EPiServerException : Property 'PageName' does not exist, can only assign values to existing properties

This error comes from that the default properties and settings has not been initialized so the system property PageName can not be accessed. To be able to do it you have to code like this:
IContentRepository contentRepository = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance();
StandardPage standardPage = contentRepository.GetDefault(parent);

And here you have to mock the IContentRepository and then find out what .GetDefault() do and fake it also and also find out how to get hold of the IContentRepository without using ServiceLocator.

Like I said before, I think the EPiServer community would be much more into testing if there were more examples on how to do it and for example the Alloy site would also have a test project.

I am now trying to make a test project in a Alloy site with some examples on how to do unit and integration tests for EPiServer, but it is going slow, since I am no expert on it. Anyone like to help me?

Arve Systad
Arve Systad Jan 20, 2015 09:35 AM

Some fixes were done to PageData-testing a few patches ago, that might be relevant.

Also, here goes the "enforce clean code"-principle: I don't think Page-models should contain methods like GetFooterText(), since that's a purely presentational thing. It should also be depending on the localization service, since you should never hard code strings for the GUI like that. I tend to look at ContentTypes as dumb model classes, just containing data. When presenting them or passing them around, I don't want functional routines within them. Separate your content model away from the presentation, and put that "GetFooterText" method in some form of helper class (or maybe even a partial view accepting PageData as its model).

And also, by using Moq, or other mocking frameworks, you can easily mock your page data objects:

var pageMock = new Mock();
pageMock.Setup(x => x.PageName).Returns("My mocked page name");

Then, when you need it from a content repo/loader:

var contentLoaderMock = new Mock();
contentLoaderMock.Setup(x => x.Get(It.IsAny()).Returns(pageMock);

And to use it, you new up your service class, controller or what have you:

var controllerInTest = new ArticleController(contentLoaderMock);

assuming your articleController has a constructor like this (which it then should have):

public void ArticlePage(IContentLoader contentLoader) {
_contentLoader = contentLoader;
}

..so it's really the combination of frameworks for mocking and testing and using proper dependency inversion you can do all these things. If you fail at one of them, testing will be really cumbersome in many cases.

Arve Systad
Arve Systad Jan 20, 2015 09:38 AM

And also, a point is to never (as far as you can) access the ServiceLocator directly in your service classes, controllers or the like. That will end up forcing you to mock everything that class wants every time you want to test it, instead of just passing in the things you need.

Just make sure all your service classes are registered into StructureMap (which EPiServer uses), and you can just demand them as constructor parameters in your own service classes or controllers.

Henrik Fransas
Henrik Fransas Jan 20, 2015 09:57 AM

Thanks Arve
I totally agree with that FooterText should not be there, it was a example from another developer I found in another thread.

Thank for examples, I will try to write a couple of test in the Alloy Site and published that to github so others can contribute with more (and probably better examples).

Thomas Schmidt
Thomas Schmidt Jan 21, 2015 12:28 PM

You should definitely unit test complex logic in your own code, but trivial code that is not a cross cutting could potentially be left out if budget does not allow for it. Like others are saying, initial development time might increase but it pays of in the end and over time as the project evolves. Also it becomes very easy to see if the code you are writing is unit testable or not, if it isn't then your design is flawed.

Unit testing EPiServer APIs does not make sense, you should be able to assume that EPiServer is well tested. The problem is there is zero documentation on the exceptions that EPiServer can throw which makes it a lot harder when it comes to EPiServer.

As for integration testing directly against EPiServer; I highly recommend that, it massively improves development efficiency when you are working with for instance Commerce. It is so much faster to start EPiServer up in an integration test (2 key presses if you are a resharper user for instance) and test/develop your code than it is to start the site up, click a billion
buttons to trigger the code you actually wants to test.

Also +1 to what Arve said, you should not be using your page models as viewmodels or have methods on them, use proper viewmodels.

Henrik Fransas
Henrik Fransas Jan 21, 2015 12:37 PM

Thanks Thomas.
I totally agree with you that complex logic should be tested. The code example I showed was not my code, I was taking it from another thread as an example, and I agree that it should be in a viewmodel.

Arve Systad
Arve Systad Jan 21, 2015 12:54 PM

A small clarification though: You can include the content models in your view models (to allow for on page editing-functionality), but not use them as view models directly.

Henrik Fransas
Henrik Fransas Jan 21, 2015 12:56 PM

True Arve, that is how we use ViewModels when we use them, so in the view it is possible to write something like:
@Html.PropertyFor(x => x.CurrentPage.MainContentArea, new { CssClass = "row", Tag = Global.ContentAreaTags.TwoThirdsWidth })

Henrik Fransas
Henrik Fransas Jan 22, 2015 01:22 PM

Here is example on how to do unit testing for EPiServer Find
http://world.episerver.com/blogs/Henrik-Fransas/Dates/2015/1/how-to-do-unit-testing-on-episerver-find/

Emma Thomas
Emma Thomas Jan 27, 2015 11:04 AM

Excellent post, thanks Henrik, I am looking forward to see your test examples on github.

Henrik Fransas
Henrik Fransas Jan 27, 2015 11:34 AM

@Emma, you can see some examples here:
http://world.episerver.com/blogs/Henrik-Fransas/Dates/2015/1/how-to-do-unit-testing-on-episerver-find/

Emma Thomas
Emma Thomas Jan 27, 2015 01:05 PM

Great, thank you. Will you be writing few more examples without episerver Find?

Please login to comment.
Latest blogs
Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog

Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog

Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog