Construct pseudo objects using the isolation framework for unit testing

Source: Internet
Author: User
ArticleDirectory
    • 1. Simple business scenarios
    • 2. What is a pseudo object?
    • 1. Problems with hand-writing simulated objects and pile objects
    • 2. Use the isolation framework
I. pseudo objects

 

1. Simple business scenarios

There is a File MonitorProgramThere is a way to check the validity of the file name. During the check, if the file name is invalid, you need to call the remote web service to record the log, such as remote web service call exception, send an email to a specified recipient (the business scenarios described in this sentence are not very large in actual development ).

 

2. What is a pseudo object?

In the preceding business scenario, the main program of file monitoring needs to be implemented by itself, while remote web services and mail services are external services on which the monitoring program depends, during our development and testing, we may not be able to directly call the service, or it may be too costly to call the service (think about why it is too costly ?), How can we perform tests at this time?

The answer is construction.Pseudo object (fake object)To replace external dependent services,Pseudo objectIt is the collective name of the pile object and the simulation object.

In our business scenario, we obviously need to construct two pseudo objects, namely the two most common basic services: log and mail services.

 

(1 ),Pile object (stub)

Definition: a pile object is a substitute for existing dependencies in the system. It can be controlled by humans. By using a pile object, you can directlyCodeTest.

Relationship with the tested object:

It can be seen that the pile object will not be asserted in the unit test.

 

(2 ),Mock)

Definition: a simulated object is used to determine whether a unit test passes or fails. It verifies whether expected interaction is performed between the tested object and the pseudo object.

Relationship with the tested object:

It can be seen that the simulated object must be asserted in unit test.

 

To sum up, we can analyze in the current scenario, if the test results are not verified for the Web Service pseudo object (that is, they are not asserted), but only to ensure that the test runs correctly, log service is a pile object. If we need to assert against the mail service and verify whether it is called correctly, mail service is a simulation object.

 

The following describes how to manually create two pseudo objects, Log service is a pile object and mail service is a simulation object. The Code is as follows:

A. logservice

 Logservice  Using System; Namespace Monitorservice { Public   Interface Ilogservice {exception extothrow { Get ; Set ;} /// <Summary>          /// Record logs          /// </Summary>          /// <Param name = "MSG"> </param>          Void Appendlog ( String MSG );} Public   Class Stublogservice: ilogservice {Public Exception extothrow { Get ; Set ;} /// <Summary>          /// Record logs          /// </Summary>          /// <Param name = "MSG"> </param>          Public   Void Appendlog ( String MSG ){ If (Extothrow! = Null ){Throw Extothrow ;} // Throw new notimplementedexception ("fake exception "); }}}

 

B. emailservice

 Emailservice  Namespace Monitorservice { Public   Interface Iemailservice { String To { Get ; Set ;} String Subject {Get ; Set ;} String Body { Get ; Set ;} /// <Summary>          /// Send an email          /// </Summary>          /// <Param name = "to"> </param>          /// <Param name = "subject"> </param>          /// <Param name = "body"> </param>          Void Sendemail (String To, String Subject, String Body );} Public   Class Mockemailservice: iemailservice { Public   String To { Get ; Set ;} Public   String Subject { Get ; Set ;}Public   String Body { Get ; Set ;} Public   Void Sendemail ( String To, String Subject, String Body) {to = to; subject = subject; body = body ;}}}

 

In the configuration file monitoring classAnalyzeInternal communication between the method and the pile object and the simulation object:

C. configfilemonitor

Configfilemonitor  Using System; Using Monitorservice. Contract; Namespace Monitorservice. impl { /// <Summary>      /// Configuration file monitoring management class      /// </Summary>      Public   Class Configfilemonitor: ifilemonitor { Public Ilogservice logservice { Get ; Set ;} Public Iemailservice emailservice { Get ; Set ;} Private   Static   Readonly   String Configfiletype =" . Config "; // Configuration file suffix          Public   Void Analyze ( String Filename ){ Try { If (Filename. Length <= configfiletype. Length) {logservice. appendlog ( String . Format (" Input filename ({0}) is too short ", Filename ));}} Catch (Exception ex) {emailservice. sendemail (" Jeffwong@cnblogs.com "," Filename check ", Ex. Message );}}}}

 

Unit Testing is as follows:

D. unittest

 Monitorservicetest_manual Using System; Using Monitorservice; Using Monitorservice. impl; Using Ninject; Using Nunit. Framework; Namespace Unittestapp {[testfixture] Public   Class Monitorservicetest_manual { Public Configfilemonitor currentfilemonitor { Get ; Set ;}Public Ilogservice logservice { Get ; Set ;} Public Iemailservice emailservice { Get ; Set ;} [Setup] Public   Void Setup (){ Using (VAR kernel = New Standardkernel ( New Servicemodule ())){ This . Currentfilemonitor = kernel. Get <configfilemonitor> (); This . Currentfilemonitor. logservice = kernel. Get <stublogservice> (); This . Currentfilemonitor. emailservice = kernel. Get <mockemailservice> () ;}} [test] Public   Void Filemonitor_inject_getinstance () {assert. isnotnull (currentfilemonitor ," Configfilemonitor is not initialized ");} [Test] Public  Void Filemonitor_logservice_inject_getinstance () {assert. isnotnull (currentfilemonitor. logservice ," Configfilemonitor stub logservice is not initialized ");} [Test] Public   Void Filemonitor_emailservice_inject_getinstance () {assert. isnotnull (currentfilemonitor. emailservice ," Configfilemonitor mock emailservice is not initialized ");} [Test] Public   Void Analyze_webservicethrows_sendemail () {currentfilemonitor. logservice. extothrow =New Notimplementedexception (" Fake exception "); VaR parameter filename =" Abc.txt "; Currentfilemonitor. Analyze (invalid filename); Assert. areequal (" Jeffwong@cnblogs.com ", Currentfilemonitor. emailservice. To); Assert. areequal (" Filename check ", Currentfilemonitor. emailservice. Subject); Assert. areequal (" Fake exception ", Currentfilemonitor. emailservice. Body ); // Assert. areequal ("fake object", currentfilemonitor. emailservice. Body ); } [Teardown]Public   Void Teardown (){ This . Currentfilemonitor = Null ;}}}

 

We use ninject to implement decoupling of service calls. In order to see the effect in a simple way, dependency injection is not directly implemented through constructors or attributes.

Through the above instructions, we can see that there are obvious differences between the two pseudo objects. Some books write many pages without worrying about them, I will introduce the similarities and differences between the two concepts, but the audience is still confused.

 

Ii. Isolation framework

 

1. Problems with hand-writing simulated objects and pile objects

When writing unit tests, without the help of frameworks and tools, some time will inevitably be spent on constructing simulation objects and pile objects. This does not mean that such work is meaningless, the main reason is that writing so many codes by hum will obviously cause the following problems:

(1) Should I write unit test code and create n pseudo objects? I don't have the hard work of TMD, but I can't write it;

(2) There are a bunch of methods, events, and attributes in some categories. In addition, there are many private methods that cannot be written;

(3) Some method parameter objects have many attributes. I don't know how many assertions need to be written before the verification is completed. I can't write it;

(4) If a simulated method is called multiple times and the status is saved, you must write a lot of code internally;

(5) I wrote a simulated object and a pile object. In another test, TMD needs to write it again. However, I cannot write it.

Everyone knows the truth. People are the least reliable. The more you write, the higher the probability of a problem, and there is no need to write a lot of identical or similar code. We will naturally think of automation, I believe that the wisdom of developers should have this awareness, and countless practices prove that writing programs is of course highly automated and best.

You want the programmer to manually write all unit tests, okay, someone will definitely first go online to see the news, pour a cup of water, and by the way, check whether your favorite beauty is thirsty and you want to see the beauty. You must first see it and then you will want to come and see it again...

Unit Testing is very important, but not many people are willing to put effort into writing, for no reason, just lazy.

 

2. Use the isolation framework

Based on the sample code in Section 1, we use the isolation framework rhino mocks (which has a long history and rich data) to see how to automatically generate pile objects and simulation objects for unit testing through the isolation framework:

(1) construct the logservice of the pile object

StublogserviceVaR mock =NewRhino. mocks. mockrepository (); var logservice = mock. Stub <ilogservice> (); currentfilemonitor. logservice = logservice; Assert. isnotnull (currentfilemonitor. logservice ,"Configfilemonitor stub logservice is not initialized");

 

(2) construct the simulated object emailservice

MockemailserviceVaR mock =NewRhino. mocks. mockrepository (); var emailservice = mock. dynamicmock <iemailservice> (); currentfilemonitor. emailservice = emailservice; Assert. isnotnull (currentfilemonitor. emailservice ,"Configfilemonitor mock emailservice is not initialized");

 

(3) unit test

A. Classic recording-Playback Model

 Rhinomockstest_recordplayback          /// <Summary>          /// Use recording-Playback model test example          /// </Summary> [Test]Public   Void Rhinomocks_analyze_webservicethrows_sendemail () {var mock = New Rhino. mocks. mockrepository (); var logservice = mock. Stub <ilogservice> (); var emailservice = mock. dynamicmock <iemailservice> (); Using (Mock. Record () {logservice. appendlog (" Input filename(abc.txt) is too short "); Lastcall. Throw ( New Notimplementedexception (" Fake exception ")); // Record the specified log and throw an exception } Currentfilemonitor. logservice = logservice; currentfilemonitor. emailservice = emailservice; var folder filename =" Abc.txt "; Currentfilemonitor. Analyze (invalid filename); Mock. Verify (emailservice ); // Verifyall () or verify (stub) will not make any assertions on the pile object, they are only applicable to the simulation object }

 

B. Set-operation-assertions (Aaa) Model

 Rhinomockstest_aaa          /// <Summary>          /// Set-operation-assert model test example (arrange-act-assert AAA)          /// </Summary> [Test] Public  Void Rhinomocks_analyze_webservicethrows_sendemail_aaa () {var mock = New Rhino. mocks. mockrepository (); var logservice = mock. Stub <ilogservice> (); logservice. Benchmark CT (SVC => SVC. appendlog (" Input filename(abc.txt) is too short "). Throw ( New Notimplementedexception (" Fake exception ")); // Record the specified log and throw an exception VaR emailservice = mock. dynamicmock <iemailservice> (); currentfilemonitor. logservice = logservice; currentfilemonitor. emailservice = emailservice; Mock. replayall ();// Move to Operation Mode VaR parameter filename =" Abc.txt "; Currentfilemonitor. Analyze (invalid filename ); // Use rhino mocks to assert Emailservice. assertwascalled (M => M. sendemail (" Jeffwong@cnblogs.com "," Filename check "," Fake exception ")); // Succeeded              // Emailservice. assertwascalled (M => M. sendemail ("it@cnblogs.com", "FILENAME check", "fake exception"); // failed }

 

Whether it is the classic recording-Playback mode or the arrange-act-assert (set-operation-asserted) syntax, it is quite readable to use rhino mocks to generate a pseudo object. Currently, the AAA mode may be more popular, but it is also concise. This example is just a test. If you are interested, please download this demo to test it.

Other mature isolation frameworks include Moq, typemock isolator, easymock. net, nmock, nunit. mocks, and so on. At present, Moq is rapidly emerging with its concise, convenient, and painless learning curve. Based on some materials, it is found that some of its writing methods are indeed easier and more readable than rhino mocks, especially Moq's natural AAA writing method, rhino mocks joins AAA for reference. For more information about how to use Moq, see the official documentation.

The code in the example is very simple. In actual development, unit testing is far more difficult and complex than in theory, for example, there are few or no historical systems left behind by documents, the project contains intricate business logic, uncommon Application System domain knowledge, and frequent patch changes, in this way, it is especially important to use frameworks and tools to write out excellent unit tests. Otherwise, maintaining the unit test code is also a great burden.

 

Download Demo: unittestapp

 

Refer:

Http://code.google.com/p/moq/

Http://hibernatingrhinos.com/open-source/rhino-mocks

Http://www.codeproject.com/Articles/10870/New-Version-Of-Rhino-Mocks

<C # Test-driven development>

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.