ASP. NET Series: Unit test

Source: Internet
Author: User
Tags httpcontext nopcommerce xunit

Unit testing can effectively improve our work efficiency and quality in many aspects, such as coding, design, debugging and refactoring. There are many open source projects available for reference and learning on GitHub, and Nopcommerce, Orchard, and Microsoft's ASP. NET MVC, Entity Framework related most projects can be used as a reference for learning unit testing . NET unit Test Art and C # test-driven Development are good learning materials.

1. Benefits of Unit Testing

(1) Unit test help design

Unit testing forces us to focus on the implementation of the interface, the process of writing unit testing is the process of designing the interface, so that the process of unit testing is we write the implementation process. I have always felt that this is the most important benefit of unit testing, and let us focus on the interface rather than the details of the implementation.

(2) Unit test help coding

Application unit testing allows us to proactively eliminate and reduce unnecessary coupling, although the starting point may be to more easily complete unit testing, but the result is usually the type of responsibility more cohesive, the coupling between types is significantly reduced. This is known as an effective means of improving the quality of the coding and is an effective means of improving the coding level of developers.

(3) Unit test help debugging

Code that applies unit tests can quickly locate the source of the problem when it is debugged.

(4) Unit test help refactoring

Refactoring of existing projects is a better choice, starting with writing unit tests. Refactoring from local code, extracting interfaces for unit testing, and then refactoring at the type and hierarchy levels.

Unit testing is designed, coded, and debugged to make it an essential skill for software development stakeholders.

2. Application Unit Testing

Unit testing is not a simple way to understand the use of test and simulation frameworks like Xunit and MOQ, and we must first have enough knowledge of the code we are writing. Usually we look at the code as some kind of static interrelated type, the dependency between the types uses the interface, the implementation class implements the interface, at run time through the custom factory or uses the dependency injection container management. A unit test typically calls a method or property in a method to be tested by using an assert assertion to detect the running results of a method or property, and there are usually several test codes that we need to write.

(1) Test domain layer

The domain layer consists of Poco, which directly tests the public behavior and attributes of the domain model. The following code extracts the unit test fragment from the customer entity in:

(2) Test application layer

The application layer mainly consists of the service interface and the implementation, the application layer relies on the infrastructure components to exist in the interface mode, the interfaces of these infrastructures are simulated by mock mode.

(3) Test presentation layer

The representation layer's dependency on the application layer is represented by the invocation of the service interface, and the instance of the dependent interface is obtained by means of a mock.

(4) Test infrastructure layer

The infrastructure layer tests typically involve the configuration file, Log, HttpContext, SMTP, and other system environments, often using mock mode.

(5) Integration testing using unit tests

First, through the interface dependencies between the system, through the dependency injection container to obtain the interface instance, in the configuration of dependencies, already implemented partial direct configuration, pseudo-implementation of the partial configuration of the mock framework generated by the instance object. As the system is continuously implemented, the dependent mock objects are replaced with the implementation objects.

3. Use assert to determine logical behavior correctness

The Assert assertion class is the core class in the Unit test framework, in which a static method of the Assert class is used to verify that the logical behavior is correct by verifying the running results of the method or property to be tested, and the should method is typically an assert wrapper in the form of an extended method.

(1) Assert assertion

If you have used the System.Diagnostics.Contracts.Contract assert method, it is easier to assert static classes provided in unit test frameworks such as Xunit, as well as conditional judgments, and the Assert classes in the Unit test framework provide a number of more specific methods such as Assert.true, Assert.notnull, assert.equal and other convenient condition judgment and information output.

(2) Should extension method

Using the should extension method reduces both the use of parameters and semantics, while providing a more user-friendly hint when testing fails. Xunit.should has stopped updating, the should component has reused the assert implementation of Xunit, but has also stopped updating. The shouldly component uses its own implementation, a project that is still being updated, and StructureMap uses shouldly in the unit test. It is also easy to manually wrap the assert, and the following code extracts the custom extension method from the assert of NUnit in Nopcomnerce 3.70.

namespacenop.tests{ Public Static classtestextensions { Public StaticT shouldnotnull<t> ( ThisT obj)            {assert.isnull (obj); returnobj; }         Public StaticT shouldnotnull<t> ( ThisT obj,stringmessage)            {assert.isnull (obj, message); returnobj; }         Public StaticT shouldnotbenull<t> ( ThisT obj)            {assert.isnotnull (obj); returnobj; }         Public StaticT shouldnotbenull<t> ( ThisT obj,stringmessage)            {assert.isnotnull (obj, message); returnobj; }         Public StaticT shouldequal<t> ( ThisT Actual,Objectexpected)            {assert.areequal (expected, actual); returnactual; }        ///<summary>        ///Asserts that and objects is equal. ///</summary>        ///<param name= "actual" ></param>        ///<param name= "expected" ></param>        ///<param name= "message" ></param>        ///<exception cref= "assertionexception" ></exception>         Public Static voidShouldequal ( This ObjectActualObjectExpected,stringmessage)        {assert.areequal (expected, actual); }         Public StaticException Shouldbethrownby ( ThisType Exceptiontype, Testdelegate testdelegate) {            returnassert.throws (Exceptiontype, testdelegate); }         Public Static voidShouldbe<t> ( This ObjectActual) {assert.isinstanceof<T>(Actual); }         Public Static voidShouldbenull ( This ObjectActual)        {Assert.isnull (Actual); }         Public Static voidShouldbethesameas ( This ObjectActualObjectexpected)        {Assert.aresame (expected, actual); }         Public Static voidShouldbenotbethesameas ( This ObjectActualObjectexpected)        {Assert.arenotsame (expected, actual); }         Public StaticT castto<t> ( This Objectsource) {            return(T) source; }         Public Static voidShouldbetrue ( This BOOLsource)        {assert.istrue (source); }         Public Static voidShouldbefalse ( This BOOLsource)        {assert.isfalse (source); }        /// <summary>        ///compares the strings (case-insensitive). /// </summary>        /// <param name= "actual" ></param>        /// <param name= "expected" ></param>         Public Static voidAssertsamestringas ( This stringActualstringexpected) {            if(!string. Equals (actual, expected, stringcomparison.invariantcultureignorecase)) {varMessage =string. Format ("expected {0} but is {1}", expected, actual); Throw Newassertionexception (message); }        }    }}
4. Use the mock mode

Using the mock pattern is used to solve the external dependency problem that cannot be tested in the code to be tested, and more importantly, low coupling is achieved through interface abstraction. For example, using the ConfigurationManager object through an abstract Iconfigurationmanager interface seems to add more code to the unit test. In fact, we don't usually care if the after-go configuration is a config file read through the ConfigurationManager static class, we only care about the configured value, At this point, you can use Iconfigurationmanager to not rely on the specific ConfigurationManager type, but also to use other implementation classes that implement the Iconfigurationmanager interface when the system needs to be extended.

Main steps for using mock objects:

(1) Use interface dependencies instead of primitive type dependencies.

(2) through the adaptation of the original type to implement the above interface.

(3) The implementation class that manually creates the interface is used for unit testing, or it can be dynamically generated by a mock frame during unit testing.

A manually created implementation class is often called the fake class, which implements the interface fully, so that the fake object can be used in multiple tests, but the fake class typically does not contain logical behavior but simply returns the desired result. Often called stub classes generated using the mock framework, only the methods required to invoke the current test need to be simulated, often with logical judgments based on parameters, and different results are returned.

Both the fake object and the stub object are named from the angle in which the class was created, and the test class directly relies on the interface implemented by the fake object or stub object.

Resolves the dependency problem for the class being tested and also addresses the case where assert assertions cannot be used directly on the tested method. At this point we need to use assertions on the fake object or stub object, if the fake object or stub object does not meet the requirements, we need to build other mock objects, the tested code does not rely on this mock object, which is used only for assert assertion use, Usually we refer to the mock object used by assert as a mock object.

A stub or mock object is a mock object that is differentiated only by dependencies and purpose, even if you do not understand the meanings and differences that are involved and do not create problems when you use them. For example, test mail delivery, we usually do not directly in the test code to apply the assert, we will be on the simulated STMP server object to apply assert to determine whether the message was successfully received, the SmtpServer mock object is a mock object instead of a stub object. For example, writing logs, we can usually directly in the ILogger interface related methods to apply Assert judgment is successful, the Logger object is the stub object is also a mock object.

5. Unit testing common frameworks and components

(1) Unit Test framework.

Xunit is currently the most popular. NET unit Testing framework. NUnit appeared earlier is widely used, such as Nopcommerce, orchard and other projects from the beginning has been using the NUnit. Xunit is now a better choice than NUnit, and a series of Microsoft projects such as ASP. NET MVC can be seen from GitHub using the Xunit framework.

(2) Mock frame

MOQ is currently the most popular mock frame. Microsoft projects such as Orchard and ASP. NET MVC use MOQ. Nopcommerce uses Rhino Mocks. Nsubstitute and Fakeiteasy are the other two widely used mock frames.

(3) The mock component sent by mail Netdumbster

The Netdumbster component can be obtained through NuGet, which provides an Simplesmtpserver object for simulating the mail delivery environment.

Usually we can't send a message using assert directly, using netdumbster we can apply assert to the messages received by the analog server.

 Public voidsendmailtest () {simplesmtpserver server= Simplesmtpserver.start ( -); Iemailsender Sender=NewSmtpadapter (); Sender. SendMail ("[email protected]","[email protected]","subject","Body"); Assert.equal (1, server.    Receivedemailcount); Smtpmessage Mail= (smtpmessage) server. receivedemail[0]; Assert.equal ("[email protected]", Mail. headers[" from"]); Assert.equal ("[email protected]", Mail. headers[" to"]); Assert.equal ("subject", Mail. headers["Subject"]); Assert.equal ("Body", Mail. messageparts[0].    Bodydata); Server. Stop ();}

(4) HttpContext mock-up component Httpsimulator

The same can be obtained through nuget, by initiating an HTTP request using the Httpsimulator object, and Httcontext the object to a usable state during its life cycle.

Since HttpContext is closed and cannot be used with MOQ simulations, we typically use the following code snippet:

PrivateHttpContext Sethttpcontext () {HttpRequest HttpRequest=NewHttpRequest ("","http://mySomething/",""); StringWriter StringWriter=NewStringWriter (); HttpResponse HttpResponse=NewHttpResponse (StringWriter); HttpContext Httpcontextmock=NewHttpContext (HttpRequest, HttpResponse); HttpContext.Current=Httpcontextmock; returnhttpcontext.current;}

After using Httpsimulator we can simplify the code to:

using New Httpsimulator ()) {  }

This is important for testing the DbContext life cycle of programs that use IOC containers and entityframework, and the life cycle of dbcontext must be consistent with HttpRequest, so it is necessary to test the IOC container for its life cycle.

6. The difficulty of using unit tests

(1) Unwilling to pay the cost of learning and change existing development habits.

(2) There is no habit of thinking, the wrong unit test when the framework of the study.

(3) Unit tests are applied later in the project, that is, the benefit of not getting the unit tests and the fact that the code is not being tested is a misunderstanding of unit testing.

(4) Refusing to consider efficiency, extensibility and decoupling, only consider the implementation of data and functions.

ASP. NET Series: Unit test

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.