猿问

如何模拟 objectOutputStream.writeObject()?

我想做什么

我有一个服务器类,它故意没有传递给它的参数,并希望使用Mockito对其进行测试。

如果你想在Github上查看完整的源代码:

服务器.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 的典型情况,当遇到最终方法时。


服务器测试.class


我尝试了什么

在其他文章中有人建议,可以通过实现接口来更改所测试类的签名来规避最终问题,因此无论如何都会模拟它。ObjectInput


然而,在所提出的方法中,当不作为参数传递给被测类时,不清楚如何操作。ObjectOutputStream


另外,当您直接控制一个 的响应时,如何操作,如我的结构所示,这对于 TCP 客户端/服务器应用程序来说并不罕见。ObjectInputStreamObjectOutputStreamcase


到目前为止,我的印象是我的测试可以工作,如果不是签名中的最终关键字。如果我错了,请在这里纠正我。ObjectOutputStream


问:“为什么我不将流传递到服务器?答:因为我在其他任何地方都不需要它们。


这真的是最后的努力,如果有必要,我会的,但我宁愿不这样做。


UYOU
浏览 94回答 2
2回答

慕码人2483693

您可以在类中提供一个函数,该函数读取输入流并模拟它而不是方法:ServerfromClientObjectInputStream.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

牧羊人nacy

问题中的误解我知道Mockito不能模拟最终类,例如ObjectOutputStream和ObjectInputStream。> 这正是我遇到问题的地方。到目前为止,我的测试在线NullPointerException失败when(server.fromClient.readObject()).thenReturn(0x2);.这是 Mockito 的典型情况,当遇到最终方法时。您的 NPE 不是由于 Mockitos 无法模拟方法引起的。NPE 的原因是您的成员变量,并且在测试代码尝试访问它们时未初始化。finalfromClienttoClient但即使如此,它们也会使用真实依赖项的实例进行初始化,而不是使用模拟,因此Mockito无论如何都无法配置它们。您从根本上误解了 .你永远不会嘲笑你的班级被测试。您始终模拟受测类的依赖项,并将受测代码的实际依赖项替换为此模拟。mocks在其他文章中有人建议,可以通过实现接口来更改所测试类的签名来规避 -问题,因此无论如何都会模拟它。finalObjectInput不。您建议针对接口进行编程,而不是针对具体类进行编程,并使用依赖注入/控制反转。没有人建议你测试的类(这是你的类)应该实现任何接口。Server违反 OOP 最佳实践问:“为什么我不将流传递到服务器?答:因为我在其他任何地方都不需要它们。这真的是最后的努力,如果有必要,我会的,但我宁愿不这样做。我们做OOP的主要原因之一是我们代码的可重用性。单元测试是代码最明显的首次重用。在测试代码时遇到困难会发现缺乏可重用性。在这一点上,你的类根本不应该被“重用”并不重要。这是您的通用编码方法,它将使您将来遇到麻烦。Server您的类存在一些违反 OOP 最佳实践的行为:Server封装/信息隐藏。您最有可能将成员变量和包声明为私有,以便能够从我们的测试中访问它们。这在两个方面存在问题:fromClienttoClient您的代码永远不应该公开成员变量等实现细节(除了它是DTO/ValueObject)。切勿为启用测试而显式修改生产代码。关注点分离任何类(提供业务逻辑)都应该有一个单一的责任。依赖项的实例化是一个完全不同的责任,这与您的业务逻辑不同。因此,您的类不应负责执行此实例化。Server我知道这有点教条,但它会带来更好的可测试性,因此更好的可重用代码。再说一遍:如果你的代码目前根本不打算重用,这并不重要。结论恕我直言,您真正的问题是:如何在不适应OOP最佳实践的情况下测试我的代码?在这种情况下,您应该通过使用PowerMock明确地放弃糟糕的设计。
随时随地看视频慕课网APP

相关分类

Java
我要回答