我如何单元测试这个inputStream已经关闭?

mog*_*lol 13 java junit unit-testing mockito

我有一个Runnable方面:

    public void run() {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            //more stuff here
        } 
        catch (Exception e) {
            //simplified for reading
        }
        finally {
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {}
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

如何inputStream.close()调用我的测试?我目前正在使用Mockito和JUnit.我知道注入inputStreamin是一个想法,但我不希望在run?()调用之前使用资源,因此它是一个局部变量.那么如何以允许我测试close是否被调用的方式重新设计我的代码?

Evg*_*eev 16

如果我理解正确的任务,它可能是这样的

static boolean isClosed;

public void run() {
    InputStream inputStream = null;
    try {
        inputStream = new FileInputStream(file) {
            @Override
            public void close() throws IOException {
                isClosed = true;
                super.close();
            }
        };
        // more stuff here
Run Code Online (Sandbox Code Playgroud)


Syn*_*sso 6

由于没有理由将InputStream暴露在此方法范围之外,因此存在测试问题.

但我认为你并不直接关心InputStream被关闭.你想测试一下,因为你被告知这是一个很好的做法(而且确实如此).但我认为你真正关心的是流被打开的负面影响.有什么影响?

尝试修改此方法,使其不关闭流,然后执行多次.你有内存泄漏,或文件句柄或其他一些tomfoolery?如果是这样,你有一个合理的测试.

或者,继续展示一个装饰好的InputStream,它可以告诉你它是否已经关闭.使其受到保护.这是"不纯",但务实的做法.

  • +1,他明白了,重要的是测试失败的影响而不是类的内部行为. (3认同)

小智 5

要检查是否调用了close()方法,可以使用Mockito.spy()创建可以记忆调用的代理对象.Spy委托对底层InputStream的所有调用,只记住发生的事情:

InputStream inputStreamSpy = Mockito.spy(inputStream);
// a code that is expected to close your stream goes here ...
Mockito.verify(inputStreamSpy).close();
Run Code Online (Sandbox Code Playgroud)

实际上,这并不能解决注入InputStream实例的问题.看起来你需要某种工厂,可以为你打开一个流,你可以在单元测试中模拟那个工厂.我们将这个工厂称为FileSystem:

public class FileSystem {
    public FileInputStream newFileInputStream(File file) {
        return new FileInputStream(file);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以注入FileSystem的实例,并且在执行run方法之前它不会使用资源:

public void run() {
    InputStream inputStream = null;
    try {
        inputStream = fileSystem.newFileInputStream(file);
        //more stuff here
    } 
    catch (Exception e) {
        //simplified for reading
    }
    finally {
        if(inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {}
        }
    }
}

@Test
public void runShouldCloseInputStream() {
    InputStream inputStream = ...
    InputStream inputStreamSpy = Mockito.spy(inputStream);
    FileSystem fileSystemMock = Mockito.mock(FileSystem.class);
    when(mockFileSystem.newFileInputStream(Mockito.any(File.class)))
        .thenReturn(inputStreamSpy);

    MyRunnable instance = new MyRunnable(mockFileSystem);
    instance.run();

    verify(inputStreamSpy).close();
}
Run Code Online (Sandbox Code Playgroud)

间谍可以做更多然后只是听,你可以教它使用Mockito.when()改变行为,就像你对常规模拟一样.