Quantcast
Viewing latest article 4
Browse Latest Browse All 9

Test System.out with JUnit

Edit: see also the follow up article on how to Test log4j with JUnit if you’re interested in specifically testing log output.

Just occasionally, it can be useful to verify output to

System.out
 in a unit test. For example, if you’re testing application logging or if you’re using log output to sense some other behaviour. It can be tricky to properly mock behaviour of
System.out
 but fortunately, it is possible to test
System.out
 with JUnit.

Here’s a simple Spring MVC Controller:
private Logger log = Logger.getLogger(SignupController.class);
@Autowired private UserDetailsManager userDetailsManager;
@Autowired private PasswordEncoder passwordEncoder;

@RequestMapping(value = CONTROLLER_URL, method = RequestMethod.POST)
public String signup(@Valid @ModelAttribute SignupForm signupForm, Errors errors) {

    if (errors.hasErrors()) {
        log.warn("Oh no! Signup failed as there are validation errors.");
        return null;
    }

    // Hash the password
    String hashedPassword = passwordEncoder.encode(signupForm.getPassword());

    // Create the account
    UserDetails userDetails = new User(signupForm.getName(), hashedPassword, ENABLED);
    userDetailsManager.createUser(userDetails);

    log.info("Success! Created new user " + userDetails.getUsername());

    return VIEW_SUCCESS;
}

Ignoring tests for the obvious behaviours here (method return value, password hashing and account creation), lets focus instead on testing the log output. We want to verify the results of the

log.warn
 and
log.info
 calls. Our options are:
  1. Replace the
    Logger
     instance with a mock. This would be easier if it were injected (
    @Autowired
    ) into the class but it’s still possible – Powermock can do this.
  2. Replace
    System.out
     with a mock.
    Logger
     writes to console via
    System.out
     by default so if we can mock
    System.out
    , we can verify logging. This technique is shown below.

Replacing System.out with a mock

By default, System.out is a PrintStream, typically connected to the console. It can however be replaced with any other instance of

PrintStream
 using System.setOut(PrintStream out). If we replace it with a
PrintStream
 backed by a byte array, we can verify all writes to
System.out
 by inspecting the byte array. Here’s an example of a test that uses this technique:
@RunWith(MockitoJUnitRunner.class)
public class SignupControllerTest {

    private static final String NAME = "smith";
    private static final String PASSWORD = "password";

    @InjectMocks private SignupController controller = new SignupController();

    private PrintStream sysOut;
    private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();

    @Before
    public void setUpStreams() {
        sysOut = System.out;
        System.setOut(new PrintStream(outContent));
    }

    @After
    public void revertStreams() {
        System.setOut(sysOut);
    }

    @Test
    public void testSuccessIsLogged() {
        SignupForm form = populateForm(NAME, PASSWORD);
        Errors noErrors = new DirectFieldBindingResult(form, "form");

        controller.signup(form, noErrors);

        assertThat(outContent.toString(), containsString("Success!"));
    }
}

Note that we maintain the original instance of

System.out
 and put it back after the test is finished. This is required so that subsequent tests properly log to console rather than our byte array.

Using a JUnit @Rule

This technique is fine for a one-off test but will require copy and pasting of the

@Before
 (setup) and
@After
 (teardown) methods if it’s to be used in other tests. To make this technique reusable in other tests, we can use a JUnit
@Rule
.
System.out
 is an external resource that needs to be set up before a test and torn down afterward so let’s sublcass JUnit’s ExternalResource class:
public class SystemOutResource extends ExternalResource {

    private PrintStream sysOut;
    private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();

    @Override
    protected void before() throws Throwable {
        sysOut = System.out;
        System.setOut(new PrintStream(outContent));
    }

    @Override
    protected void after() {
        System.setOut(sysOut);
    }

    public String asString() {
        return outContent.toString();
    }
}

This simplifies our test class which now looks like this:

@RunWith(MockitoJUnitRunner.class)
public class SignupControllerTest {

    private static final String NAME = "smith";
    private static final String PASSWORD = "password";

    @InjectMocks private SignupController controller = new SignupController();

    @Rule public SystemOutResource sysOut = new SystemOutResource();

    @Test
    public void testSuccessIsLogged() {
        SignupForm form = populateForm(NAME, PASSWORD);
        Errors noErrors = new DirectFieldBindingResult(form, "form");

        controller.signup(form, noErrors);

        assertThat(sysOut.asString(), containsString("Success!"));
    }
}

The test code for these examples is in GitHub:

The post Test System.out with JUnit appeared first on Don't Panic!.


Viewing latest article 4
Browse Latest Browse All 9

Trending Articles