繁体   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