24

I'm currently setting the groundwork for an ASP.Net MVC application and I'm looking into what sort of unit-tests I should be prepared to write. I've seen in multiple places people essentially saying 'don't bother testing your views, there's no logic and it's trivial and will be covered by an integration test'.

I don't understand how this has become the accepted wisdom. Integration tests serve an entirely different purpose than unit tests. If I break something, I don't want to know a half-hour later when my integration tests break, I want to know immediately.

Sample Scenario : Lets say we're dealing with a standard CRUD app with a Customer entity. The customer has a name and an address. At each level of testing, I want to verify that the Customer retrieval logic gets both the name and the address properly.

To unit-test the repository, I write an integration test to hit the database. To unit-test the business rules, I mock out the repository, feed the business rules appropriate data, and verify my expected results are returned.

What I'd like to do : To unit-test the UI, I mock out the business rules, setup my expected customer instance, render the view, and verify that the view contains the appropriate values for the instance I specified.

What I'm stuck doing : To unit-test the repository, I write an integration test, setup an appropriate login, create the required data in the database, open a browser, navigate to the customer, and verify the resulting page contains the appropriate values for the instance I specified.

I realize that there is overlap between the two scenarios discussed above, but the key difference it time and effort required to setup and execute the tests.

If I (or another dev) removes the address field from the view, I don't want to wait for the integration test to discover this. I want is discovered and flagged in a unit-test that gets multiple times daily.

I get the feeling that I'm just not grasping some key concept. Can someone explain why wanting immediate test feedback on the validity of an MVC view is a bad thing? (or if not bad, then not the expected way to get said feedback)

4
  • 1
    "To unit-test the repository, I write an integration test" Wait... what? That's not a unit test of the repository. You are automating the test for it, but code under test still includes the DAL and the database. To unit test the repository you have you isolate it like you have for your business rules.
    – StuperUser
    Oct 12, 2011 at 12:22
  • Unit testing the view rendered as expected is just unit testing that your templating engine works. That's like unit testing your compiled C contains certain chunks of machine code, your unit testing the compiler not your code.
    – Raynos
    Oct 12, 2011 at 13:06
  • 2
    @Raynos Respectfully, I'm going to have to disagree. If I (or another developer) mistakenly wires up the UI to render one data attribute in the UI field for another (For example, 'First Name' in the 'Last Name Field', that has nothing to do with the templating engine, nor is it a DAL or BR issue.. it's clearly a problem that would only be exposed on the view. Oct 12, 2011 at 14:51
  • 1
    @PeterBernier you have a good point, but I find it difficult to define the line between "testing whether the compiler works" and "testing whether my code works". Not to mention that tests for the UI are tightly coupled to the UI. Any changes to the UI cause the tests to fail. You can't really do any kind of refactoring of the UI without causing a test to fail.
    – Raynos
    Oct 12, 2011 at 15:49

5 Answers 5

11

Simple UI testing is easy enough in ASP.NET MVC. Essentially all you have to do is assert that the returned HTML contains the elements you need. While this ensures that the HTML page is structured the way you expect, it doesn't fully test the UI.

Proper web UI testing requires a tool like Selenium that will use browsers on your machine and ensure that the JavaScript and HTML are working properly in all browsers. Selenium does have a client/server model so that you can have a set of virtual machines with Unix, Mac, and Windows clients and the set of browsers common to those environements.

Now, a well designed MVC (pattern, not framework) application puts the important logic in the models and controllers. In short, the functionality of the application is tested when you test those two aspects. Views tend to only have display logic and are easily checked with visual inspection. Due to the thin processing in the view and the bulk of the application being well tested, many people don't think that the pain of testing the view layer outweighs the benefit gained by it.

That said, MVC does have some nice facilities to check the DOM returned by the request. That reduces the pain quite a bit for testing the view layer.

5
  • 1
    "Essentially all you have to do is assert that the returned HTML contains the elements you need." This is exactly what I'm trying to do and it's turning out to be non-trivial. Can you point to a link where that'll work with a specific controller action as opposed to simply rendering a control? (I've worked through a couple of write-ups, but RenderPartial isn't accomplishing what I want to do without significant overhead..) Jan 5, 2011 at 15:59
  • You'll want to check out mvccontrib.codeplex.com (MVC Contrib). This provides help that wasn't built in to the core language, and was recommended in the book "Test-Drive ASP.NET MVC" (pragmatic programmers). I still think Selenium is a better match for View testing, though. Jan 5, 2011 at 16:06
  • TestHelper (MVC Contrib): mvccontrib.codeplex.com/… Jan 5, 2011 at 16:08
  • Selenium (in my case Selenium RC) is what I'll be using for my integration tests. What I want is for a failure to happen before that point. Jan 5, 2011 at 18:05
  • 2
    @Peter: Your comment about your efforts being "non-trivial" is exactly the reason that unit testing views is frowned upon. Consequently, a typical strategy is to make the views as thin as possible (i.e. containing no business logic), so that most of the unit testing can take place somewhere else (generally in the ViewModel). The views themselves can be verified by visual inspection, or with a UI testing tool like Selenium. Jan 12, 2011 at 17:29
7

I wouldn't say it's frowned upon. Rather, that sentiment is the result of the fact that unit-testing MVC views (at least of the aspx variety) is quite hard because aspx views have too much dependency on WebForms, which are themselves quite untestable. So the argument goes that it's not worth the effort because the views tend to be not that complicated.

Of course views can get quite complicated so it's your choice.

4
  • 3
    ASP.NET MVC views aren't tied to Webforms, far as I'm aware. Isn't one of the big points going for ASP.NET MVC that it's not Webforms?
    – Adam Lear
    Jan 5, 2011 at 15:28
  • My point of view is that it takes more human effort to write the integration tests to cover the UI, than it would to write real 'unit-tests' to covers the views. That's why I'm trying to understand some of the resistance that seems to be out there towards writing unit-tests for the views. Jan 5, 2011 at 15:36
  • @Anna Aspx views are built on top of WebForms. They derive from the System.Web.UI.WebControls.Page class, use <asp:ContentPlaceholder> controls etc. The way MVC executes them avoids a lot of the Page execution pipeline typically associated with WebForms but it still uses a lot of WebForms stuff under the covers.
    – marcind
    Jan 5, 2011 at 16:18
  • If you use a different view engine (such as razor) you should be able to move farther away from the Webforms engine. Dec 9, 2011 at 19:29
6

I'm not sure that it is frowned upon. Testability is one of the key benefits of using ASP.NET MVC. Check out Steve Sanderson's blog for more info on this.

He also wrote the hands-down, best ASP.MVC book (IMO) out there. Not only does he teach MVC, but he goes above and beyond to teach best practices around it, including testing practices.

I'm thinking I need to clarify a bit on unit testing views -- you can build unit tests around the result returned from the controller (ActionResult, etc.). You'll still need to do other testing for the actual UI and UI interaction.

1
  • "You'll still need to do other testing for the actual UI and UI interaction." That's exactly my point of the question.. why do the UI tests suddenly become part of 'other testing' (ie, integration testing). I'd seen alot of the Steve Sanderson content and that's sort of what got me started down this path, basically trying to replicate what he's doing with his 'MvcFakes' project and running into into issues with his code being written for older MVC releases.. Jan 5, 2011 at 15:32
1

You can learn how to test the View returned by a controller action, how to test the View Data returned by a controller action, and how to test whether or not one controller action redirects you to a second controller action by check out the following URL, describe in this brief article about Testing View Data in MVC.

0

Short Answer

Unit tests cannot guarantee that no one will change the source code. That is not what they are for. They should depend on input ( or no input in some cases), and outcome. This also has to be deterministic. Please note to unit test a non-deterministic piece of code, one must create mocks to make it appear as deterministic.

The unit test you are asking for, as I will explain below in this answer, is to guarantee no one will change the source code; which of course is not possible.

Long Answer

Imagine you wrote an application that takes an input and puts double quotes around it. For example, you give it Test and it returns "Test". To unit test that, you may write a few tests like below:

TestNoSpace
Test With a Space
Test With "Quotes"

You run your tests, and the expected output matches. Ok great! That will work that way for as long as no one changes the source code. If they change the source code but change only the structure of the source code, the tests will pass. If they change it, refactor it, the output will still be the same and you don't really care if they refactor it (change the structure) for as long as the output is the same. The tests will fail though if someone changed it from adding quotes to adding single quotes. Pretty trivial points we can agree on.

Now let's go to your question about testing MVC Views. That same idea applies, that given a certain input, you expect a certain output. For example, you may have a view:

<label>First Name:</label><Label>@Model.FirstName</Label>

Is that the input or the output? It is neither. It is source code. Similar to code you wrote for adding quotes which may look like:

public string AddQuotes(string value)
{
    return "\"" + value + "\"";
}

You would never write a unit test to make sure no one changes the above code, would you? You only would write a test to make sure that it behaves correctly: Given a string, it returns the string with quotes around it.

So now I ask you the same question: Why would you write a test to make sure no one changes the structure of the cshtml code?

You may say I don't care if the structure of cshtml code is changed, I just want to make sure the Model.FirstName is put in the correct place. Well, again, how would someone do that? Because the only way I can think for someone doing that is to change the structure of cshtml code, nonetheless, it is change to the source code.

At this point you may ask, "OK fine! But what if I wanted to test that given the current source code in cshtml, I expect a certain output by the Razor engine?" And the answer to that is: Do you have a doubt that Razor has a bug and you want to ensure it does things correctly? In other words, you would be unit testing Razor; which is obviously not something you are after or something one would do--unless, of course, one is building the Razor engine. You may still say, "I don't care if the label is moved down and more elements are added, I just want to make sure the Model.FirstName is mapped to the correct element". And to that I will say: What if the developer changed it from a label to a span or an input, now what? You see where I am going with this.

If you pay close attention, the cshtml is input for Razor engine. Testing to make sure cshtml is not changed would be the same as if you wrote a test to make sure no one changes an input to the AddQuotes method above: It just makes no sense.

So again, the question comes down to this:

  1. What is it that you want to test?
  2. What is the input?
  3. What is the expected outcome?
  4. Give an input, can we deterministically expect an outcome?

The answer to the above questions would be:

  1. I want to test that the Model.FirstName is always mapped to the correct label.
  2. I, yes I am talking about myself, honestly don't know. Is it the Model you pass to the view? If yes, you can unit test that at the controller to make sure the Model being passed is correct. Is it the CSHTML file? But that is source code that is passed as input to Razor and we would not test Razor as I have already explained above.
  3. I, yes I am talking about myself, honestly don't know. Is it the _Layout.cshtml plus the cshtml file in question plus the inline JavaScript and CSS? What is it?
  4. Well we cannot answer this unless we can answer 2 & 3 above.

Therefore, testing such stuff in the UI, not all stuff in the UI (some can be unit tested, see below), is not only frowned upon, but it just does not make sense; at least it is not a good idea. The cshtml file is source code and it makes no sense to write unit tests to make sure no one changes the source code.

OK, so we should not test UI?

No, that is absolutely not what I am saying! I am saying we cannot test that a certain field is mapped to the correct element in cshtml or any other UI because that depends on the structure of the source code.

You should test the behavior of UI using something like https://www.selenium.dev/ to ensure when things are clicked, it behaves properly or whatever other tests you deem fit.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.