Introduction to power mocks and examples of their use

Source: Internet
Author: User
Tags assert static class throw exception

Related framework

JUNIT4, Mockit, Powermock

Related maven dependencies

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
< dependency>
    <groupId>org.powermock</groupId>
    <ARTIFACTID>POWERMOCK-MODULE-JUNIT4 </artifactId>
    <version>1.6.5</version>
    <scope>test</scope>
</ dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactid >powermock-api-mockito</artifactId>
    <version>1.6.5</version>
    <scope>test </scope>
</dependency>

Code base
The subsequent test code is for the following class

Import org.springframework.beans.factory.annotation.Autowired;

    public class Usercontroller {@Autowired private userservice userservice;
        public boolean addUser (Userdto userdto) {int added = Userservice.adduser (userdto);
        if (added <= 0) {return false;
        else {return true;
            } public boolean deluser (int id) {try {userservice.deluser (ID);
        return true;
        catch (Exception e) {return false;
    } public void Saveuser (Userdto userdto) {userservice.saveuser (userdto);

        public int Countuser () {Userdto ud = new Userdto ();

        int count = 0;
        if (Ud.getid () > 0) {count = 1;
    return count;
        public boolean moduser (Userdto userdto) {int moded = Userservice.moduser (userdto);
    Return Verifymod (moded);
Private Boolean verifymod (int moded) {        if (moded <= 0) {return false;
        else {return true; }
    }
}
Public interface UserService {
    int addUser (userdto userdto);

    int deluser (int id) throws Exception;

    int Moduser (userdto userdto);

    void Saveuser (Userdto userdto);
}
public class Userdto {
    private int id;

    public int getId () {return
        ID;
    }

    public void setId (int id) {
        this.id = ID;
    }
}
public class Filehelper {public
    static string GetName (string name) {return
        ' A_ ' + name;
    }
}

Note: All test classes must add the following annotation

@RunWith (Powermockrunner.class)
@PrepareForTest ({usercontroller.class, filehelper.class})
@ Powermockignore ("javax.management.*")

Where: @RunWith (Powermockrunner.class) : Indicates that Powermockerrunner is used to run test cases, otherwise powermock preparefortest ({ Usercontroller.class}): All classes that need to be tested, listed here, are separated by commas @PowerMockIgnore ("javax.management.*"): In order to resolve using Powermock, prompt ClassLoader error

java.lang.noclassdeffounderror:com.ibm.mq.jms.mqqueueconnectionfactory$ $EnhancerByCGLIB $$7cb492ab ( Initialization failure) at
    java.lang.J9VMInternals.initialize (j9vminternals.java:140)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0 (Native method) at
    Sun.reflect.NativeMethodAccessorImpl.invoke ( NATIVEMETHODACCESSORIMPL.JAVA:60) ...
    

How @Autowired properties are injected

public class Usercontrollertest {
    @Mock
    private userservice userservice;

    @InjectMocks
    Private Usercontroller UC = new Usercontroller ();
}

A few points to note: The above method will mock out a user service object and inject it into the Usercontroller instance of UC . The new Usercontroller () in the back of UC can also be unwanted.

Mock Common Method

    @Test public
    void Testadduser () throws Exception {
        Userdto ud = new Userdto ();
        Powermockito.when (Userservice.adduser (UD)). Thenreturn (1);
        Can not stubs like this
        //Powermockito.doreturn (1). When (Userservice.adduser (UD));
        Boolean result = Uc.adduser (UD);
        Assert.assertequals (result, true);
    }

In the code above, there are two points to note: Userservice.adduser () and Uc.adduser () use the same parameter values to allow the Powermock to match the parameters at run time. ( at the end of this article, we'll talk about how to do fuzzy matching parameters.) the value returned by Thenreturn () needs to be consistent with the type of the return value declared by the Userservice.adduser () method, otherwise the compilation will be faulted. A mock statement cannot be written as follows:

Powermockito.doreturn (1). When (Userservice.adduser (UD));

Otherwise the exception will be reported:

Org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
...

Mock throw exception

    @Test public
    void Testdeluser () throws Exception {
        int todelete = 1;
        Powermockito.when (Userservice.deluser (Todelete)). Thenthrow (New Exception ("Mock Exception"));
        Boolean result = Uc.deluser (todelete);
        Assert.assertequals (result, false);
    }

There are a few things to note: if the Deluser () method in the User service throws a checked exception, then Thenthrow () needs to throw the new exception () or its subclasses if Delu The Ser () method throws the unchecked exception, then the Thenthrow () needs to throw the new RuntimeException () or its subclasses

Mock static methods

    @Test public
    void Mockfilehelper () {
        powermockito.mockstatic (filehelper.class);
        Powermockito.when (Filehelper.getname ("Lucy")). Thenreturn ("Lily");
        Assert.assertequals (Filehelper.getname ("Lucy"), "Lily");
    }

There are a few things to note: You need to add filehelper.class to the @PrepareForTest Note to call powermockito.mockstatic (), which is Filehelper.class

Mock return method with void value

    @Test public
    void Testsaveuser () throws Exception {
        userdto userdto = new Userdto ();

        Way One:
        powermockito.donothing () When (UserService, "Saveuser", userdto);

        Way two:
        powermockito.donothing (). When (UserService). Saveuser (userdto);

        Uc.saveuser (userdto);
    }

Mock Private Method method one
PS: This method also describes the method of mocking the value of a private field.

    @Test public
    void Testmoduser () throws Exception {
        Userdto ud = new Userdto ();
        int moded = 1;

        Powermockito.when (Userservice.moduser (UD)). Thenreturn (moded);

        Usercontroller uc2 = Powermockito.mock (usercontroller.class);

        Assign a value to a private field that does not have a setter method.
        whitebox.setinternalstate (UC2, "UserService", userservice);

        Because the Moduser () method is being tested,
        //So, when calling this method, you should let it call the real method instead of the mock off Method
        Powermockito.when (Uc2.moduser (UD)). Thencallrealmethod ();

        The private method of Verifymod () is called in the Moduser () method, so you need to mock off
        powermockito.when (UC2, Verifymod, moded). Thenreturn (True) ;

        Boolean result = Uc2.moduser (UD);

        Assert.assertequals (result, true);
    }

Note that the UC2 here is a mock up, not a member variable in the Usercontrollertest class. UC Method II

    @Test public
    void TestModUser2 () throws Exception {
        Userdto ud = new Userdto ();
        int moded = 1;

        Powermockito.when (Userservice.moduser (UD)). Thenreturn (moded);

        For UC monitoring
        UC = Powermockito.spy (UC);
        When UC Verifymod is executed, the
        Powermockito.when (UC, "Verifymod", moded) will be mock off. Thenreturn (true);
        Boolean result = Uc.moduser (UD);

        Assert.assertequals (result, true);
    }

Using the Spy method avoids the execution of member functions in the class being measured, that is, a mock out of a private method that you do not want to be executed.

Test Private methods (note: test, not mock) method one

    @Test public
    void Testverifymod () throws Exception {
        //Get Method Object, Method Method
        = Powermockito.method ( Usercontroller.class, "Verifymod", int.class);
        Invoke method to execute
        boolean result = (Boolean) Method.invoke (UC, 1);
        Assert.assertequals (result, true);
    }
Method Two
    @Test public
    void TestVerifyMod2 () throws Exception {
        //execute Boolean result by Whitebox
        = Whitebox.invokemeth OD (UC, "Verifymod", 1);
        Assert.assertequals (result, true);
    }

Mock new Object

    @Test public
    void Testcountuser () throws Exception {
        Userdto ud = new Userdto ();
        Ud.setid (1);

        Powermockito.whennew (Userdto.class). Withnoarguments (). Thenreturn (UD);

        int count = Uc.countuser ();

        Assert.assertequals (count, 1);
    }

Mock return static method with void value ( This is late replenishment, so no related complete code provided ) method one

"Xxxutil" is the class name
//"Xxxstaticmethod" is the method name of the static method//
This assumes that "Xxxstaticmethod" requires two parameters, one is int and one is String 
  powermockito.donothing () When (Xxxutil.class, "Xxxstaticmethod", 1, "MySQL");
Method Two
In this way, all the "static void" methods that need to be emulated are listed to
powermockito.donothing (). When (Xxxutil.class);
Xxxutil.xxxstaticmethod (1, 2);

Mock the same method, return a different value ( this is a late complement, so do not provide the relevant complete code )

The code to be tested
DatabaseMetaData dbmetadata = Connection.getmetadata ();
ResultSet Schemaset = Dbmetadata.getschemas ();
while (Schemaset.next ()) {
    Schemalist.add (schemaset.getstring ("Table_schem"));
}

The above code, we want to let schemaset return true, so that the test code can enter the while loop. But we can't keep it returning true, otherwise, while it will fall into a dead loop. How to deal with this kind of demand? Please see:

Connection Connection = Powermockito.mock (connection.class);
DatabaseMetaData DatabaseMetaData = Powermockito.mock (databasemetadata.class);
ResultSet ResultSet = Powermockito.mock (resultset.class);

Powermockito.when (Connection.getmetadata ()). Thenreturn (DatabaseMetaData);
Powermockito.when (Databasemetadata.getschemas ()). Thenreturn (ResultSet);

Critical step
Powermockito.when (Resultset.next ()). Thenreturn (True, false);

Powermockito.when (resultset.getstring ("Table_schem")). Thenreturn ("mock schema");

In the key steps above, theThenreturn () method returns two values, one is true, and the other is false. It means that when next () is called for the first time, it returns true, and the second and second calls return false. This satisfies our needs.

Mock generics ( This is late replenishment, so no relevant complete code is provided )

The code to be tested
list<node> nodes = new arraylist<> ();
Getallchildren () is a recursive method, and the return value is void
nodeservice.getallchildren (nodeId, nodes);

In the code above, we generally mock the Getallchildren () method in nodeservice when we do test, but this can lead to nodes The contents of this list are always empty (because its return value is void). In order to satisfy the need to nodes the content of the Getallchildren () in the case of being mock off, we can mock the ArrayList constructor . However,ArrayList is a generic class, so when you mock its constructor, how do you specify a generic type? Method One
When you construct ArrayList, you do not specify a generic type.
PS: In this method, the type of thenodes variable must be a ArrayList, not a List.

ArrayList nodes = new ArrayList () {
    node n = new node ();
    N.setid (1);
    This.add (n);
};
Powermockito.whennew (Arraylist.class). Withnoarguments (). Thenreturn (nodes);
Method Two
By using the Powermock AnswerMechanism.
Final list<node> nodes = new arraylist<node> () {
    node n = new node ();
    N.setid (1);
    This.add (n);
};

Powermockito.whennew (Arraylist.class). Withnoarguments (). Thenanswer (New answer<list<node>> () {
    @ Override public
    list<node> Answer (Invocationonmock invocation) throws Throwable {return
        nodes;
    }
} );

Mock variable parameters

Pending mock method public
list<node> Getflowbyprjid (int prjid, Integer ... status) {
    //do something
}

Like the above method, it has a parameter called varargs, such as how this parameter should be modeled.
It's very simple, because the varargs parameter is actually treated as an array, so we just have to deal with it as follows:

When (Xxxclass.getflowbyprjid (Matchers.anyint (), (integer[)) Matchers.anyvararg ()). Thenreturn (nodelist);

Mock Final method

final is as mock as the normal method, but it needs to add its class to the @PrepareForTest annotation, i.e.

@PrepareForTest ({xxxclasswithfinalmethod.class})

Xxxclasswithfinalmethod obj = mock ( Xxxclasswithfinalmethod.class);
When (Obj.xxxfinalmethod ()). Thenreturn (xxxxxxx);

Otherwise, it would be confusing to report an exception like the following (because we are obviously invoking the method on the object being mock ):

When () requires a argument which has to be ' a mock '

Mock private internal static class object

public class ClassA {
    private static class Innerclassa {
        private Innerclassa (String name) {
            this.name = name;< c9/>}

        @Override public
        void Run () {
            //do something
        }
}}

For the above class, if we want to test the Innerclassa class's run method, what do we do?
First of all, because Innerclassa is a private inner class, we can't mock it like this (compilation will be an error, this inner class is not visible):

Classa.innerclassa AAA = mock (ClassA.InnerClassA.class);

In this case, the idea is to get the Innerclassa constructor by reflection, and then generate an object.
So how to do it. Whitebox can help you achieve this:

Class clazz = Whitebox.getinnerclasstype (ClassA.class, "Innerclassa");
Constructor constructor = Whitebox.getconstructor (Clazz, string.class);
The constructor needs a string parameter
object = constructor.newinstance ("mock name");
Run the ' run ' method
Whitebox.invokemethod (object, "Run");

Mock Super keyword

public class Dispatcherservlet extends HttpServlet {

    @Override public
    void init (ServletConfig config) throws servletexception {
        super.init (config);

        Do some thing
    }

    @Override public
    Void Destroy () {
        //do Something
    }
}

For the init method of the class above, one of the difficulties in testing is how to mock out init in the parent class. Because it's not like the way we call it in other cases, it's not easy to mock in this way by when (). XXX () . In this case, it is the turn to suppress method to come forward, the following directly give a mock way:

@Test public
void Testinit () throws Exception {
    Dispatcherservlet ds = Spy (new Dispatcherservlet ());

    Use ' method () ' To get the ' init ' method which was defined in ' Genericservlet '
    //Use ' suppress () ' to suppress the ' Init "Method
    Suppress (Genericservlet.class," Init ", Servletconfig.class));

    Ds.init (mock (servletconfig.class))
    //Other Todo ...
}

Why this is genericservlet.class rather than Httpservlet.class, because init (servletconfig config) is defined in the In Genericservlet , not httpservlet .

Attention points used by spy

To be tested code public
void Getallchildren (int parentnodeid, list<node> allchildren) {
    list<node> Children = GetChildren (Parentnodeid);
    Some other logic
    allchildren.addall (children);
}

Public list<node> getchildren (int nodeId) {
    list<node> children = Nodemapper.getchildren (nodeId);
    return children;
}

My intention was to test the Getallchildren () method, in which it called the GetChildren () method, which, naturally, I prepared to GetChildren () mock off, so I wrote the following mock statement:

Private Xxxservice ns = new Xxxservice ();

NS = Spy (NS);
Nodes is the List when (Ns.getchildren (Matchers.anyint ()) that contains 2 Node
. Thenreturn (nodes);

list<node> result = new arraylist<> ();
Ns.getallchildren (1, result);

Assertequals (Result.size (), 2);

I thought it would be possible to pass the test.
But the fact is always cruel, after running, has been the error, said result.size () The value is 0 is not 2.
This I am very puzzled, ah, obviously return is the length of 2 list Ah, why it has been 0.
It was such a humble question that it took me 5 hours to check it out.
Finally, a post on the Internet to wake up, found the problem.
The problem is on the following mock statement that seems to be taken for granted:

When (Ns.getchildren (Matchers.anyint ())). Thenreturn (nodes);

It is intended to return nodes This list when the ns getchildren () is invoked and the parameter is a value of any int type. But in this case, it's just a mock. Return to nodes This list when the parameter is 0 . Specific reasons are as follows (excerpt from the Org.powermock.api.mockito.PowerMockito.doReturn () method of Javadoc)

From the explanation here, we know what our problem is, and the kind of mock written above is actually the equivalent of:

When (Ns.getchildren (0)). Thenreturn (nodes);

Because the return value of matchers.anyint () is 0.
So only when the parameter value is 0 does it return to the nodes list.

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.