Mockito(二)–執行個體篇

來源:互聯網
上載者:User

學習了基本知識後,就可以實戰了。Mockito的實際使用還是比較麻煩的。

因為在實際使用中,最常遇到的就是需要類比第三方類庫的行為。

比如現在有一個類FTPFileTransfer,實現了向FTP傳輸檔案的功能。這個類中使用了apache的ftp類

org.apache.commons.net.ftp.FTPClient;

現在測試FTPFileTransfer 這個類中的isFTPConnected方法, 希望類比無法串連ftp的情況,測試是否記錄了錯誤log:

public class FTPFileTransfer{

  //為了測試當ftp連結不上時,是否真的會記log,我們必須mock一個假的FTPClient對象,用該對象傳遞/覆蓋掉真實的FTPClient對象ftp,並強制讓這個假對象返回"無法串連",然後看是否會記log.
 private FTPClient ftp;

 private boolean isFTPConnected(){
 if (!ftp.isConnected()) {
   LOGGER.error("Disconnected from FTP.");
 }
}

因此使用mock需要解決的問題是: 如何用mock的FTP對象覆蓋掉真實代碼中調用的FTPClient。因此需要將mock對象傳遞進去。

這裡對原始碼有一個限制:

原始碼中必須使用set和get方法來設定/獲得ftp對象,這樣測試代碼可以使用set來傳遞mock對象。

或者測試代碼中寫一個方法覆蓋掉原始碼中執行個體化ftp對象的方法,且測試代碼中使用mock ftp對象。

 

下面的樣本中都假定原始碼中使用了get/set。

 

  • 建立一個返回FTPFileTransfer instance的方法。

//使用mock()方法建立一個FTP的mock對象mockedFTP。

FTPClient mockedFTP = mock(FTPClient.class);

//Stub “無法串連”

when(mockedFTP.isConnected())).thenReturn("無法串連");   

//寫一個get方法返回FTPFileTransfer的執行個體用來測試,將mockedFTP作為參數傳遞進去。同時在這個方法內部用set方法將FTPFileTransfer類中的成員變數FTPClient  ftp更改為mockFTP。

//這樣我們就得到了一個FTPFileTransfer的執行個體,同時裡面的ftp已經變成了我們希望的mockFTP。

private FTPFileTransfer getMockTaskFileTransfer(final FTPClientmockedFTP) {
        FTPFileTransfer test = new FTPFileTransfer("127.0.0.1", 8888, "//usr", "username", "password");
        test.setFTPClient(mockedFTP);
        return test;
    }

 

@Test
    public void testTransfer() throws SocketException, IOException{       
        FTPFileTransfer test = getMockTaskFileTransfer();

        //得到這個執行個體以後,就直接調用這個執行個體的isFTPConnected方法,然後去log檔案裡找有沒有我們希望的log就行了。注意此時,mockedFTP一定會返回"無法串連",所以isFTPConnected一定會記log。
        test.isFTPConnected();
    }

測試完畢~~~

  • 用subclass-and-override實現

從名字就可以看出,通過建立被測試類別的子類,覆蓋掉被測試類別的getFTPClient()方法,將mock對象傳遞進去。

 

class MockFTPFileTransfer extends FTPFileTransfer{
       
        public MockFTPFileTransfer(){
            super("127.0.0.1", 8888, "//usr", "username", "password");
        }

         //原始碼中必須使用get來獲得ftp對象,否則mock不會生效      
        @Override
        public FTPClient getFTPClient(){
            FTPClient mockedFTP = mock(FTPClient.class);
            when(mockedFTP.isConnected()).thenReturn(true);
            return mockedFTP;
        }
    }

 

@Test
    public void testTransfer() throws SocketException, IOException{       
        FTPFileTransfer test = new MockFTPFileTransfer();
        test.isFTPConnected();
    }

  • 用partial mock實現

partial mock是1.8之後的新功能。通常情況下會使用mock出來的對象完全覆蓋掉被類比的對象,對於那些沒有stub的方法,則會返回build-in 類型的預設值。

 

@Test
    public void testTransfer() throws SocketException, IOException{
        //類比一個FTPClient對象,同時stub行為。

        FTPClient mockedFTP = mock(FTPClient.class);
        when(mockedFTP.isConnected()).thenReturn(true);

 

        //spy可以類比一個real object

        //這裡的可以認為是spy在real object上包了一層,除了getFTPClient()被覆蓋掉以外,其他方法仍然是真實對象的。

        //這就是partial mock的概念: 僅僅用mock對象覆蓋來源物件的一部分,而不是全部。
        FTPFileTransfer spyFTP= spy(new FTPFileTransfer("127.0.0.1", "//usr", "username", "password"));
        when(spyFTP.getFTPClient()).thenReturn(mockedFTP);
       
        spyFTP.setFTPClient(mockedFTP);
        spyFTP.isFTPConnected();
    }

 

完畢!

大家都明白了嗎?如果沒有明白可以回帖,我會回答的。

------------------------------------------------------------------------------------------------------------------------------------

應要求發一個完整的UT

import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;

import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.transform.stream.StreamSource;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;

import static org.junit.Assert.*;
import org.apache.commons.io.FileUtils;

import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.*;

public class ASAPSoapMessageTest {
    
    ASAPSoapMessage testASAPSoapMessage= new ASAPSoapMessage();
    String testSOAPRequest = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema\"><LoginRequest xmlns=\"http://asap.schemas.tfn.thomsonreuters.com/Messages/Base/2010-03-01/\"/></s:Body></s:Envelope>";
    File testFile = new File("testFile.xml");
    String testSOAPAction = "http://fackSOAPAction/";
    String testWebService = "http://fackWebService/";
    String testReply = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><LoginResponse xmlns=\"http://asap.schemas.tfn.thomsonreuters.com/Messages/Base/2010-03-01/\"/></s:Body></s:Envelope>";
    MessageFactory mf;
    SOAPMessage SOAPRequestTest;
    SOAPMessage replySOAPRequestTest;
    
    @Before
    public void testUp() throws SOAPException{
        mf = MessageFactory.newInstance();
        SOAPRequestTest = mf.createMessage();
        replySOAPRequestTest = mf.createMessage();
    }
    
    @Test
    public void testGetSoapContent() throws IOException, SOAPException{
        
        String soapContentString = "";
            
        StreamSource prepMsg = new StreamSource(new StringReader(testSOAPRequest));
        SOAPPart sp = SOAPRequestTest.getSOAPPart();
        sp.setContent(prepMsg);
        soapContentString = testASAPSoapMessage.getSoapContent(SOAPRequestTest);
              
        assertTrue(soapContentString.equalsIgnoreCase(testSOAPRequest));
    }
    
    @Test
    public void testPrepareSOAPMessage() throws IOException{
        FileUtils.writeStringToFile(this.testFile,this.testSOAPRequest);
        assertTrue(testASAPSoapMessage.prepareSOAPMessage("testFile.xml").equalsIgnoreCase(testSOAPRequest));        
    }
    
    @Test
    public void testSendSOAPMessage() throws SOAPException, IOException{
        
        SOAPPart sp = replySOAPRequestTest.getSOAPPart();
        StreamSource prepMsg = new StreamSource(new StringReader(testReply));
        sp.setContent(prepMsg);
        
        SOAPConnection mockedSOAPConnection = mock(SOAPConnection.class);  
        when(mockedSOAPConnection.call(argThat(new IsSOAPMessage()), anyObject())).thenReturn(replySOAPRequestTest);

        
        ASAPSoapMessage spyASAPSoapMessage = spy(new ASAPSoapMessage());
        when(spyASAPSoapMessage.getSoapConnection()).thenReturn(mockedSOAPConnection);

        
        SOAPMessage replySOAPMessage= spyASAPSoapMessage.sendSOAPMessage(testSOAPRequest, testWebService, testSOAPAction);
        assertTrue(spyASAPSoapMessage.getSoapContent(replySOAPMessage).equalsIgnoreCase(testReply));    

    }
    
    class IsSOAPMessage extends ArgumentMatcher<SOAPMessage> {
        public boolean matches(Object soapMessage) {
            return soapMessage instanceof SOAPMessage;
        }
    }
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.