10

At the moment I wrote a small downloadService which let's user download a file (at the moment only excel). The code works properly, however I do not know how to write the unit test for it. That's my code:

package com.pzm.service;

import com.pzm.model.UserBillingsMock;
import com.pzm.model.report.ExcelReport;
import com.pzm.model.report.Report;
import com.pzm.model.report.ReportFactory;
import org.springframework.stereotype.Repository;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * Created by akfaz on 6/26/14.
 */

@Repository
public class DownloadService {

    private Report report;
    private List<UserBillings> userBillings;

    public void setBill(List<UserBillings> userBillings) {
        this.userBillings = userBillings;
    }

    public void download(HttpServletResponse response, String reportType) {

        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition", "attachment; filename=MyExcel.xls");

        report = new ReportFactory().create(reportType, userBillings);
        saveFile(response, report);
    }

    private void saveFile(HttpServletResponse response, Report report) {
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            report.write(outputStream);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

the unit test - tried to use Mockito, but got the exception:

unit test:

package com.pzm.service;

import junit.framework.TestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.servlet.http.HttpServletResponse;
import static org.mockito.Mockito.*;

/**
 * Created by akfaz on 7/5/14.
 */
public class DownloadServiceTest extends TestCase{

    HttpServletResponse mockResponse;
    DownloadService downloadService;

    @Before
    public void setUp() throws Exception {
        mockResponse = mock(HttpServletResponse.class);
        downloadService = new DownloadService();
    }

    @Test
    public void testDownload() throws Exception {
        downloadService.download(mockResponse, "xls");

        verify(mockResponse).getContentType();
    }
}

and the exception:

org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException: Fail to save: an error occurs while saving the package : null
    at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:500)
    at org.apache.poi.openxml4j.opc.OPCPackage.save(OPCPackage.java:1417)
    at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:179)
    at com.pzm.model.report.ExcelReport.write(ExcelReport.java:46)
    at com.pzm.service.DownloadService.saveFile(DownloadService.java:40)
    at com.pzm.service.DownloadService.download(DownloadService.java:34)
    at com.pzm.service.DownloadServiceTest.testDownload(DownloadServiceTest.java:32)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:202)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.NullPointerException
    at java.util.zip.DeflaterOutputStream.<init>(DeflaterOutputStream.java:84)
    at java.util.zip.DeflaterOutputStream.<init>(DeflaterOutputStream.java:142)
    at java.util.zip.ZipOutputStream.<init>(ZipOutputStream.java:118)
    at java.util.zip.ZipOutputStream.<init>(ZipOutputStream.java:104)
    at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:433)
    ... 28 more
pezetem
  • 2,503
  • 2
  • 20
  • 38
  • 1
    You're mocking a HttpServletResponse. Calling getOutputStream() on this mock response will thus return null, unless you tell the mock to return an OutputStream when this method is called. – JB Nizet Jul 06 '14 at 07:28
  • Could you try `Mockito.spy()` instead of `Mockito.mock()` to create your `mockResponse`? – Robby Cornelissen Jul 06 '14 at 07:29

1 Answers1

10

When you create a mock object

mockResponse = mock(HttpServletResponse.class);

by default, all its methods that have a reference type return type (minus a few special cases) return null.

So the return value of getOutputStream() in this snippet

ServletOutputStream outputStream = response.getOutputStream();

is null.

You need to set expectations and specify a return value.

when(mockResponse.getOutputStream().thenReturn(/* the value to return when that method is invoked */);

This is called stubbing.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • +1 This is the right thing to be doing. Incidentally, your first sentence isn't entirely true - there are some reference types for which the Mockito default is not null, such as collections. – Dawood ibn Kareem Jul 06 '14 at 07:41
  • @DavidWallace Any `Collection` subtype? I didn't know that. – Sotirios Delimanolis Jul 06 '14 at 07:43
  • Umm, yeah, the "few special cases" that you've mentioned in your answer are the following. (1) Methods which return any of the wrapper types will return 0 or false by default. (2) Methods which return any subtype of `Collection` will return an empty collection by default. (3) Any implementation of the standard `toString` will return the name of the mock by default. (4) Any implementation of the standard `compareTo` will return 1 by default. – Dawood ibn Kareem Jul 06 '14 at 08:40