ASP. net mvc + MVC contrib + unit testing MVC unit test

Source: Internet
Author: User

I wrote down my article after reading some articles in the unit test recently. My English is not good and it is also the first translation. I don't have much time for the manager to wait !! I hope you will not throw bricks!

Original article http://srtsolutions.com/blogs/patricksteele/archive/2009/08/23/asp-net-mvc-mvc-contrib-unit-testing.aspx
ASP. net mvc + MVC contrib + Unit Testing
One of the biggest advantages of the MVC pattern is that its layering makes code easier to test. Microsoft realizes the importance of this, so it can automatically

Create a separate test project. This is a good start, on which you can freely expand. Although a separate function (possibly a method) is available in

Mstest is easily referenced, but it requires a lot of objtects as the basis for use, requests (query strings, parameters, and so on), return

(Cookies, content pattern, headers, etc.), sessions, etc. In a real environment, these objects are processed by ISS, but in testing

In the environment, your tests are separated and cannot be referenced.
We can use ourselves to forge these basic objects, but we need to forge a lot. This is the final focus of the MVC contrib project. (This

No translation)
MVC contrib test helper
The MVC pattern is widely praised for its various advantages-easy-to-control interfaces, easy-to-use data models, and separate control areas. In addition to these, it also has more

Add convenient test functions and use the Rhino. mocks class library. In MVC contrib, you can easily initialize the following objects;

* Httprequest
* Httpresponse
* Httpsession
* Form
* Httpcontext
* And more!
In this chapter, I will create a complete example of testing Controller under ASP. net mvc. I will use these simple examples to demonstrate the entire process and add the detailed

Description.
Scenario (overall description)
First, create an ASP. net mvc project with some data submitted by users. We need to use the "Wizard-like" mode.

He wrote his personal information (name) and then submitted it. A page is returned. Although the user may submit more information than this

Here I will start with this simple method.
Design)
The first step is to collect only the most basic personal information (surname and name). After the user submits the information, we will save the basic information to cook. After logging on

His surname and name are often referenced. The user's action is the user code in the cook in the controller. The next step is to use it to get more

Detailed information.
Set up (preparation)
Prepare a new project so that you can create a unit test in it. By default, a home controller is created in the MVC project. For simple explanation

We use it to write this test during the test process.
First, we do not create a class to store the basic information of the user. Although it only uses two pieces of information, it is not only in the actual environment, so we create

Class to facilitate access to information.

Public class speakerinfo

{

Public String firstname {Get; set ;}

Public String lastname {Get; set ;}

}

Test #1
First, let's write a code that does not pass the test. We can see where the test fails. Then we can correct the code and compare the test results.

[Testmethod]

Public void speaker_withoutsessiondata_returns_emptymodel ()

{

VaR controller = new homecontroller ();

 

VaR result = (viewresult) controller. Speaker ();

VaR info = (speakerinfo) result. viewdata. model;

 

Assert. isnull (info. firstname );

Assert. isnull (info. lastname );

}
All we need to do is create a controller and call the commit method. The information submitted by the user is not collected here:

Public actionresult Speaker ()

{

Return view ();

}

Then run my test. We can see a Red Cross, because we didn't write the model of the view. Let's modify this method:

Public actionresult Speaker ()

{

Return view (New speakerinfo ());

}

Run this example. Now we can use it. Now let's improve the code,

We will store the user's basic information in cook, so that the user can directly obtain his information and determine the permissions (they cannot submit each time after login)

). If you do not log on, an empty value will be returned. Let's write it as an attribute:

Private speakerinfo

{

Get

{

VaR info = session [sessionkeys. speakerinfokey] As speakerinfo;

If (Info = NULL)

{

Info = new speakerinfo ();

}

 

Return Info;

}

Set

{

Session [sessionkeys. speakerinfokey] = value;

}

}
Check the code: you will notice that there is an object "sessionkeys" in it. I don't like to write inexplicable string constants directly, so I often write

Put some static constants in a specific folder, which will be very good for future scalability. The Code is as follows:

Public static class sessionkeys

{

Public const string speakerinfokey = "Si ";

}
Now we can write it like this, instead of a new speakerinfo:

Public actionresult Speaker ()

{

Return view (this. speakerinfo );

}
Now I ran the test and found another error. Why? This is because the session is empty. I have not run the ASP. NET service. We are

Of course, there are no cook objects for independent tests. (haha is the focus, and I want to know about it.) MVC contrib is coming to help !! Because we

This is often used, so here is a method. To simulate basic objects in ASP. NET:

Private Static homecontroller createcontroller ()

{

Testcontrollerbuilder builder = new testcontrollerbuilder ();

Return builder. createcontroller

}
This is okay (this is amazing, I don't believe it). This homecontroller is the method we have built. Of course there are

(Session, request, response ,......) Next let's change our test code:

[Testmethod]

Public void speaker_withoutsessiondata_returns_emptymodel ()

{

VaR controller = createcontroller ();

 

VaR result = (viewresult) controller. Speaker ();

VaR info = (speakerinfo) result. viewdata. model;

 

Assert. isnull (info. firstname );

Assert. isnull (info. lastname );

}
Run this test and we find it successful! Okay. Start the next test!
(It's strange. I still don't understand it ......!! Is that testcontrrollerbuiler a member of Moq)
# Test #2
This test does not check whether the basic information of the user is saved to cook and the user object is returned. This test means that after I log on successfully

Whether the information is correct in future operations. Let's start writing. Remember that once we write a pseudo cook using the above method, it will be like a real pair.

Use it as easily as possible.

[Testmethod]

Public void speaker_withsessiondata_returns_populatedmodel ()

{

VaR controller = createcontroller ();

Controller. session [sessionkeys. speakerinfokey] = new speakerinfo {firstname = "Bob", lastname =

"Smith "};

 

VaR result = (viewresult) controller. Speaker ();

VaR info = (speakerinfo) result. viewdata. model;

 

Assert. areequal ("Bob", info. firstname );

Assert. areequal ("Smith", info. lastname );

}
If you run it, you will find that it is still correct, because we have written user information to the session.
Test #3
This test checks whether the user has entered only the last name but not the first name. How can we write this test:

[Testmethod]

Public void data_posted_without_lastname_returns_error ()

{

VaR controller = createcontroller ();

VaR result = (redirecttorouteresult) controller. Speaker ("Jim ","");

VaR info = (speakerinfo) controller. session [sessionkeys. speakerinfokey];

 

Assert. areequal ("Jim", info. firstname );

Assert. areequal ("", info. lastname );

Assert. areequal (1, controller. modelstate. Count );

Assert. istrue (controller. modelstate. containskey ("lastname "));

Result. assertactionredirect (). toaction

}
Look at the last line of code. This is the extension method. It is the nmock in Lambdas mode (Oh, I like to see Lambdas, which provides us with an extension.

Display Method to implement redirecttorouteresult In the controller. The Code should be like this (No mock helpers ):
Assert. areequal ("Speaker", result. routevalues ["action"]);

Assert. areequal ("home", result. routevalues ["controller"]);
(It's interesting for foreigners not to translate)
We have no writing method to collect user information. Let's first write a simple method to replace it:

[Acceptverbs (httpverbs. Post)]

Public actionresult Speaker (string firstname, string lastname)

{

Return NULL;

}
We can see that the accepverbs attribute of the Code is added because we want to execute the Code only when the user submits the code according to the POST method in <form>. You can also see that there are two

One is "firsname" and the other is "lastname", which is a fixed parameter passing mode provided by ASP. NET.
Now let's complete our code. Although we use Cook to store basic user information, another extension property that matches it has not yet been forged, namely the error in modelstate, you should write the following code:

[Acceptverbs (httpverbs. Post)]

Public actionresult Speaker (string firstname, string lastname)

{

This. speakerinfo = new speakerinfo {firstname = firstname, lastname = lastname };

If (string. isnullorempty (lastname ))

{

Modelstate. addmodelerror ("lastname", "Last Name Is reqiured .");

}

 

Return this. redirecttoaction (C => C. Speaker ());

}
We passed the test. Now we have successfully written three of the five methods.
(It's a bit straightforward for foreigners, but it's a bit dizzy now. Isn't it possible to add the modelstate error? Do you have to generate the default project)
Test #4
This is a bit like the previous one. The name entered by him cannot be blank. Remember what we said at the beginning!

[Testmethod]

Public void data_posted_without_firstname_returns_error ()

{

VaR controller = createcontroller ();

VaR result = (redirecttorouteresult) controller. Speaker ("", "Jones ");

VaR info = (speakerinfo) controller. session [sessionkeys. speakerinfokey];

 

Assert. areequal ("", info. firstname );

Assert. areequal ("Jones", info. lastname );

Assert. areequal (1, controller. modelstate. Count );

Assert. istrue (controller. modelstate. containskey ("firstname "));

Result. assertactionredirect (). toaction

}
This piece of code seems to be good, but we will find errors. We didn't make any logic mistakes. Let's take a look at our speaker (string, string) method: (with a modelstate error missing)

[Acceptverbs (httpverbs. Post)]

Public actionresult Speaker (string firstname, string lastname)

{

This. speakerinfo = new speakerinfo {firstname = firstname, lastname = lastname };

If (string. isnullorempty (lastname ))

{

Modelstate. addmodelerror ("lastname", "Last Name Is reqiured .");

}

If (string. isnullorempty (firstname ))

{

Modelstate. addmodelerror ("firstname", "first name is reqiured .");

}

 

Return this. redirecttoaction (C => C. Speaker ());

}
We have improved the code and passed the test.
Come back and take a look at our code. It seems that there is still one missing. What if both parameters are empty. Many of these cases have been ignored. I will add this test below. Writing this test seems redundant, but the code can be modified in the future. You cannot guarantee that it will be useless at that time.

The #3 and #4 we wrote are very similar. Except the word "first" and "last" name are different, we have written two modelstate errors:
Public void data_posted_blk_returns_error ()

{

VaR controller = createcontroller ();

VaR result = (redirecttorouteresult) controller. Speaker ("","");

VaR info = (speakerinfo) controller. session [sessionkeys. speakerinfokey];

 

Assert. areequal ("", info. firstname );

Assert. areequal ("", info. lastname );

Assert. areequal (2, controller. modelstate. Count );

Assert. istrue (controller. modelstate. containskey ("firstname "));

Assert. istrue (controller. modelstate. containskey ("lastname "));

Result. assertactionredirect (). toaction

}
The test is successful! There is only one to write now!
Test #5
This test is to save the information to cook and perform the next phase when the surname and name are not empty:

[Testmethod]

Public void data_posted_to_speaker_saves_to_session_and_redirects ()

{

VaR controller = createcontroller ();

 

VaR result = (redirecttorouteresult) controller. Speaker ("Jon", "Jones ");

Result. assertactionredirect (). toaction

 

VaR info = (speakerinfo) controller. session [sessionkeys. speakerinfokey];

Assert. areequal ("Jon", info. firstname );

Assert. areequal ("Jones", info. lastname );

}
Here you may see the sessiondetails () method. Let's write it:

Public actionresult sessiondetails ()

{

Return view ();

}

After running the test, we found that there was another error. We noticed that the last row of Speaker (string, string) had directly returned the speaker (User class) without error judgment, the sessiondetails method has not been used till now. Now it is time to use it. If the input information is incorrect, it will return to the user information speaker. If the input information is correct, sessiondetails will be called:

[Acceptverbs (httpverbs. Post)]

Public actionresult Speaker (string firstname, string lastname)

{

This. speakerinfo = new speakerinfo {firstname = firstname, lastname = lastname };

If (string. isnullorempty (firstname ))

{

Modelstate. addmodelerror ("firstname", "first name is reqiured .");

}

If (string. isnullorempty (lastname ))

{

Modelstate. addmodelerror ("lastname", "Last Name Is reqiured .");

}

 

If (modelstate. Count! = 0)

{

Return this. redirecttoaction (C => C. Speaker ());

}

 

Return this. redirecttoaction (C => C. sessiondetails ());

}
Run the test. Now all our tests have passed.
Conclusion (end)

-- ===================================================== ========================================================== =
-- = About MVC contrib DLL download http://www.codeplex.com/MVCContrib

-- =
-- ===================================================== ========================================================== ====

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.