Drink Remodeling Series [9]--decomposition dependent

Source: Internet
Author: User
Tags xunit

Overview

Writing unit tests helps improve the quality of your code, and when you write unit tests, some features may depend on other code (such as calling other components).
Usually we just want to test the functionality itself and not want to test the code it depends on.

Why is it?
The goal of unit testing is to verify that the functionality is correct, but the code on which the functionality depends is outside the scope of functionality, which may be external components that the unit test cannot verify the accuracy of the external components.
When a unit test fails with an error calling "dependent code", it affects the judgment of the test result, and we cannot determine whether the function itself is correct.
Perhaps the function is correct, but the unit test will still be considered a failure when invoking a dependent code error.
If you want to test these dependent code, we should write the unit tests for the code separately.

How to solve?
"Dependent code" has become a roadblock for us to write such unit tests, and we can solve this problem in 2 ways:
1. Mock-dependent code
2. Decomposition dependencies

The power of a mock is beyond doubt, but the mock is not omnipotent, it is limited, and we cannot mock static classes in unit tests.
However, this problem can be solved by "decomposing dependencies", and this article will demonstrate these 2 ways through an example.

Example before refactoring

This code describes a scene-"breeding animals", which contains 2 classes:
Animalfeedingservice (animal husbandry Service), as well as static class feeder (breeder).
Animalfeedingservice describes the "feeding" behavior, and the feeder class describes the "supplemental food" behavior.
In raising animals, if the animal's food bowl is empty, the breeder needs to replenish the food.

<summary>///Animal Husbandry Service//</summary>public class animalfeedingservice{    private bool Foodbowlempty { Get Set } public    void Feed ()    {        if (foodbowlempty)            Feeder.replenishfood ();}    } <summary>///breeder//</summary>public static class feeder{//<summary>///    Supplemental food//    </summary> public    static void Replenishfood ()    {            }}

Unit test code (based on Xunit and rhino.mocks framework)

public class animalfeedingservicetests{    [Fact] public    void Testfeed ()    {        Animalfeedingservice Service = new Animalfeedingservice ();        Test feed () method        service. Feed ();    }}

Since feeder is a static class, we cannot mock the feeder class when writing unit tests for Animalfeedingservice, and we do not want to validate the functionality of the feeder class in this unit test.
If there is an error when calling Feeder.replenishfood (), the execution of this unit test is unsuccessful.
At the same time, the correctness of the Feed () method cannot be verified.

After refactoring

In order to be able to remove the dependency on the feeder class in the unit test, we can add the wrapper interface Ifeederservice to the feeder class, and then let Animalfeedingservice rely on the wrapper interface. So in the Animalfeedingservicetest class, we don't have to think about the feeder class.

Display Code
<summary>///Animal Husbandry Service//</summary>public class animalfeedingservice{    private bool Foodbowlempty { Get Set Public    Ifeederservice Feederservice {get; set;}    Public Animalfeedingservice (Ifeederservice feederservice)    {this        . Feederservice = Feederservice;    }    public void Feed ()    {        if (foodbowlempty)            Feederservice.replenishfood ();}    } <summary>///Feeding Service Interface    ///</summary>public interface ifeederservice{void Replenishfood ();} <summary>///Feeding Service Implementation//</summary>public class feederservice:ifeederservice{public    void Replenishfood ()    {        feeder.replenishfood ();    }} <summary>///breeder///</summary>public static class feeder{public    static void Replenishfood ()    {    }}

Unit test code (based on Xunit and rhino.mocks framework)

public class animalfeedingservicetests{    [Fact] public    void Testfeed ()    {        // Mockrepository        var mocks = new Mockrepository () based on the rhino.mocks framework        ; Mock Ifeederservice        var feederservice = mocks. Dynamicmock<ifeederservice> ();        Animalfeedingservice service = new Animalfeedingservice (feederservice);        Test feed () method        service. Feed ();    }}

After the reconstructed animalfeedingservicetests, because Ifeederservice is a mock, Ifeederservice's Replenishfood () method is not called at all, So feeder's Replenishfood () method is not called.
The Feeder.replenishfood () method is ignored when invoking the feed () method, which allows us to focus more on the logic of the Feed () method itself.

Packaging mode

The above code, in fact, is a simple packaging mode (Wrapper pattern), packaging mode is not considered a specific design mode, because in the adapter mode (Adapter pattern), Decorative mode (Decorator pattern), Proxy mode ( Proxy pattern), façade mode (facade pattern) and so on have used the packaging class, these design patterns I am not specifically described here. Typically, wrapper classes are used to encapsulate other classes or component, and the wrapper class can provide convenience for upper-level calls and make the use of the underlying class or component more secure.

The UML structure of the packaging pattern is broadly as follows. It consists of 3 parts: Caller (Client), wrapper (Wrapper), Component (Component)

To talk about the concept of "decomposition dependency", according to this structure, we can clearly know: in order to relieve the dependence between the client and component, we added a wrapper on the client and component, so that the client relies on wrapper. Please note that since we are more inclined to interface-oriented programming, this wrapper is usually a wrapper interface. To give a common example, when we are writing some APIs, we only need to expose the API specifications (name, input and output parameters) to the client via wrapper, and the internal implementation of the API is isolated by wrapper.

Drink Remodeling Series [9]--decomposition dependent

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.