簡體   English   中英

如何模擬objectOutputStream.writeObject()?

[英]How to mock objectOutputStream.writeObject()?

我想做什么

我有一個服務器類,故意沒有將任何參數傳遞給它,並且想用Mockito測試它。

如果您想在Github上查看完整的源代碼:

Server.class

public class Server extends Thread {
    private Other other;
    ObjectInputStream fromClient;
    ObjectOutputStream toClient;


    public Server(){
        this.other = new Other(foo, bar);
    }


    @Override
    public void run(){
        try{

            ServerSocket serverSocket = new ServerSocket(1337);
            Socket socket = serverSocket.accept();

            fromClient = new ObjectInputStream(socket.getInputStream());
            toClient = new ObjectOutputStream(socket.getOutputStream());

            while(true) {
                int command = (Integer) fromClient.readObject();
                switch (command) {
                    case 0x1:
                        //add
                        //...
                        break;
                    case 0x2:
                        //get server data
                        toClient.writeObject(other.getSomething());
                        break;
                    case 0x3:
                        //delete
                        //...
                        break;
                    default:
                        break;
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        Thread t = new Server();
        t.start();
    }
}

問題

我知道Mockito無法模擬最終類,例如ObjectOutputStream和ObjectInputStream。

這正是我遇到問題的地方。

到目前為止,我的測試失敗,並在網上顯示NullPointerException

when(server.fromClient.readObject()).thenReturn(0x2);

當運行最終方法時,這對於Mockito是典型的。

ServerTest.class

@Test
    void run() throws IOException, ClassNotFoundException {
        //given
        Other other = new Other(foo, bar);
        Server server = mock(Server.class);

        //when
        when(server.fromClient.readObject()).thenReturn(0x2);

        server.start();

        //then
        verify(server).toClient.writeObject(other.getSomething());

    }

我嘗試了什么

其他文章中 ,建議可以通過實現接口ObjectInput更改被測試類的簽名來規避最終的問題,因此無論如何都要對其進行模擬。

但是,在提出的方法中,當不將ObjectOutputStream作為參數傳遞給被測類時,尚不清楚如何操作。

此外,如我的case結構所示,當讓ObjectInputStream直接控制ObjectOutputStream的響應時如何操作,這對於TCP客戶端/服務器應用程序來說並非不常見。

到目前為止,我仍然認為我的測試可以工作,而不是ObjectOutputStream簽名中的final關鍵字。 如果我錯了,請在這里糾正我。

問: “為什么不將流傳遞到服務器?” 答:因為我在其他任何地方都不需要它們。

這樣做實際上是最后的努力,如果需要,我會這樣做,但我寧願不這樣做。

您可以在Server類中提供一個從fromClient輸入流讀取並模擬它的函數,而不是ObjectInputStream.readObject()方法:

public class Server extends Thread {
     private Other other;
     ObjectInputStream fromClient;
     ObjectOutputStream toClient;


     public Server(){
         this.other = new Other(foo, bar);
     }

     Object readObject() throws ClassNotFoundException, IOException {
         return fromClient.readObject();
     }

     //...

因此您的測試將如下所示:

    @Test
    void run() throws IOException, ClassNotFoundException {
        //given
        Other other = new Other(foo, bar);
        Server server = mock(Server.class);

        //when
        when(server.readObject()).thenReturn(0x2);

        server.start();

        //then
        verify(server).toClient.writeObject(other.getSomething());
    }

同樣的事情也可以應用於toClient流。

您的問題中的誤解

我知道Mockito無法模擬最終類,例如ObjectOutputStream和ObjectInputStream。 >這正是我遇到問題的地方。

到目前為止,我的測試失敗,並在網上顯示NullPointerException

當(server.fromClient.readObject())。thenReturn(0X2);.

當運行最終方法時,這對於Mockito是典型的。

您的NPE 並非由Mockitos無法模擬final方法引起。 使用NPE的原因是,當測試代碼嘗試訪問成員變量fromClienttoClient它們不會被初始化。

但即使,他們將與實際依賴性的實例初始化,而不是與嘲笑 ,這樣會的Mockito無法反正配置它們。

你從根本上誤解的目的和技術含義mocks 永遠不會嘲笑要測試的課程。 您總是模擬被測類的依賴關系 ,並用此模擬物替換被測代碼的實際依賴關系。

在其他文章中,建議可以通過實現接口ObjectInput更改被測試類的簽名來規避final問題,因此無論如何都要對其進行模擬。

沒有。

建議您針對接口進行編程,而不是針對具體類進行編程,並建議使用依賴注入/控制反轉

沒有人建議您在測試中的類(您的Server類)實現任何接口。

違反OOP最佳做法

問:“為什么不將流傳遞到服務器?”
答:因為我在其他任何地方都不需要它們。

這樣做實際上是最后的努力,如果需要,我會這樣做,但我寧願不這樣做。

我們進行OOP的主要原因之一是代碼的可重用性

單元測試是最明顯的代碼重用。 測試您的代碼有困難,這表明缺乏可重用性。 在這一點上,您的Server類根本不打算“重用”並不重要。 這是您的通用編碼方法,將來會讓您遇到麻煩。

您的Server類違反了OOP最佳做法:

  • 封裝/信息隱藏。

    您最有可能將成員變量fromClienttoClient 聲明為私有 ,以便能夠從我們的測試中訪問它們。 這有兩個方面的問題:

    1. 您的代碼不應公開成員變量之類的實現細節 (除非它是DTO / ValueObject)。
    2. 您絕對不應顯式修改生產代碼以啟用測試。
  • 關注點分離

    任何類(提供業務邏輯)都應承擔單個責任 除了業務邏輯之外,實例化依賴項是完全不同的責任。 因此,您的Server類不應負責執行此實例化。

    我知道這有點教條,但是它會導致更好的可測試性和可重用性的代碼。 再說一遍: 當前是否不打算重用您的代碼並不重要。

結論

恕我直言,您真正的問題是: 如何在不采用OOP最佳實踐的情況下測試我的代碼?

在這種情況下,您應該使用 PowerMock 明確將您的投降提交至不良設計。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM