简体   繁体   中英

how can I write a test for void methods with junit in Java especially they do not return a value?

I have here a simple program to resolve hostname to ip address and vice versa and do not have any idea,how can I test these two void methods with junit, as long they don't return any values. I need help with junit test, because my void methods don't return any thing.


    /*
 * inside this interface we have two function,that 
 * resolve ip to host-addr and vice versa
 */
public interface DnsFunctions {

    public void resolveHostToIp();

    public void resolveIpToHost();
}



and here ist the main code for the Dns resolver where the void methods are invoked inside the switch case:


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class DnsResolver implements DnsFunctions {

    static BufferedReader br;
    static DnsFunctions df = new DnsResolver();
    static boolean status = true;

    public static void main(String[] args) throws NumberFormatException, IOException {

        StringBuilder sb = new StringBuilder();
        sb.append("Welcome to DNS-resolver page :");
        sb.append("\n*select (1) to resolve Hostname to IP-Address");
        sb.append("\n*select (2) to get the Host name from inputed IP-Address");
        sb.append("\n*select (3) for EXIT");

        System.out.println(sb);

        /*
         * depending on the inputted value from user /1-2 or 3/ the suitable function
         * will be called
         */

        while (status) {
            br = new BufferedReader(new InputStreamReader(System.in));

            int inputedValue = Integer.parseInt(br.readLine());

            switch (inputedValue) {

            case 1:

                df.resolveHostToIp();
                status = true;
                break;

            case 2:
                df.resolveIpToHost();
                status = true;
                break;

            case 3:
                status = false;
                System.out.println("GoodBye :)");
                break;

            }
        }

    }

    @Override
    /*
     * 
     * @parameter value
     */
    public void resolveHostToIp() {
        try {
            System.out.println("\n Enter Hostname:");
            String hostName = br.readLine();

            InetAddress address = InetAddress.getByName(hostName);
            System.out.println("Hostname :" + address.getHostName());
            System.out.println("IP:" + address.getHostAddress());
        } catch (Exception e) {
            System.out.println("ERROR :(" + e.getMessage());
        }

    }

    @Override
    /*
     * @parameter value
     */
    public void resolveIpToHost() {
        try {
            System.out.println("\n Enter IP address");
            String ip_add = br.readLine();
            InetAddress ia = InetAddress.getByName(ip_add);
            System.out.println("IP: " + ip_add);
            System.out.println("Host Name: " + ia.getHostName());
        } catch (IOException e) {
            System.out.println("ERROR :(" + e.getMessage());
        }

    }

}


The problem is that the only observable effect produced by these methods is that they write to System.out . Nothing more. And the interface doesn't indicate that in any way.

One option to test the implementation would be to redirect and observe System.out . If, however, there's ever a different implementation which doesn't write to System.out then that would fail the test. It'll work if you're reluctant to change the code, but has its downsides as well.

If on the other hand you can change the code, then you can make the methods more testable by having them return their values instead of printing their values. Then in the main() method you would print the returned values.

The benefit here becomes cleaner testability by separating the concern of producing the value from the concern of outputting the value. They're two different things and should be handled separately, even if both are small and simple.

From a purist unit testing perspective, you'd likely also want to encapsulate the dependency on the static methods in InetAddress , likely by wrapping it in a mockable object and injecting a mock for the test.

It approaches an interesting problem though, because once both of these dependencies are removed then the methods effectively don't do anything and the value of testing them in the first place quickly approaches zero. They'd just be a pass-through to the mock that's being injected, nothing more.

For the immediate purpose, I'd recommend removing the System.out dependency and keeping that solely in the main() method, having the methods return their values. And then re-classifying your unit tests as "integration tests" with a known dependency on InetAddress .

This is (almost) untestable with jUnit. And pretty useless also, when we look into the implemention.

The main blocker: It's a unit test so we don't want to invoke the real getByName method on InetAdress . We'd have to mock it. But it's a static method and jUnit can't mock that.

The challenges: we'd have to replace System.out and System.in by methods where we can pass our test data (into the System.in ) and assert the output (from the mocked System.out ).

The challenges are solvable, the static method problem requires rewriting your code.

You can mock InetAddress.getByName(ip_add); call and then add a check like - assertEquals("StringThatShouldComeUpOnConsole", outContent.toString());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM