5

I have created one utility module which converts one file format to another one. File i.e. test.abc will be converted to i.e. test.pqr and internal format of files are totally different. This module involves nearly 5 different file formats.

Internally, it uses one native tool and I do not have any control over it. It converts the file and my utility class just sends commands to this tool and manipulates files.

public class Utility {

     public File convertIt(File file, Format to) {
        // Check extension
        // Execute command of native tool accordingly
        // Return resulting file
     }
}

I am not able to conclude how should I write unit test cases for this utility. Because each format is different and on production every file will be inserted by user. Moreover, there are lot of attributes some are mandatory and some are optional. and it may be possible one file format may use it or may not.

Currently what I am thinking is, in test case resources, I can supply all the input files and resulting files which I already have. Then I can just compare output of each call with existing output file. But I think this will not cover all the scenarios and I may need more files for testing.

- resources
       |- input
            |- input1.abc
            |- input2.pqr
            |- input3.xyz
       |- output
            |- output1.xyz
            |- output2.abc
            |- output3.pqr

My questions,

  • Is this approach correct ?
  • Is there any other better approach ?
  • What is the best practice to test such methods which manipulates files ?
  • 4
    Do you want to test your code or the "native tool" which converts the file? – wonderbell Aug 22 '16 at 10:30
  • @wonderbell I want to test my code which is basically a wrapper of native tool. – CoderCroc Aug 22 '16 at 11:49
  • "nearly 5 different file formats". So four file formats, then? Is the method convertIt simplified here, ie you do not appear to pass the format to be converted to in as a parameter. Presumably the real method has a number of parameters for for attributes you speak of? – David Arno Aug 22 '16 at 11:57
  • @DavidArno Yes I missed one parameter which is an enum, states about the format to be converted in. – CoderCroc Aug 22 '16 at 11:59
  • is your converter bidirectional (i.e. there is convertHtml2Rtf and convertRtf2Html) so that you can check that convertHtml2Rtf(convertRtf2Html(someRtf)) == someRtf and convertRtf2Html(convertHtml2Rtf(someHtml))) == someHtml with a list of sample files? – k3b Aug 22 '16 at 15:46
3

I want to test my code which is basically a wrapper of native tool.

Perfect. If you wanted to test the native tool you wouldn't do that via your code in any case. So you don't have to create input files or output files. All you have to do is ensure that the native tool is called with the correct arguments.

One way is to create an Executor class that can execute a command, and pass it into your converter.

class Converter {
    private Executor executor;

    public Converter(Executor executor) {
        this.executor = executor;
    }

    convertIt() {
        command = ...
        executor.execute(command);
    }
}

}

Then use use Mockito to create a mock Executor for testing. Use junit to make it easy to create and run test methods.

import org.junit.Test;
import static org.mockito.Mockito.*;

class ConverterTest {
    private Executor executor;

    @Before
    public void setup() {
         executor = mock(Executor.class);
    }

    @Test
    public void TestAtoB() {
        String expectedCommand = ...;
        Converter converter = new Converter(executor);
        converter.convert(...);
        verify(executor).execute(expectedCommand);
    }
}
2

First of all, a pedantic point. As these tests are calling an external utility that reads and writes files, these tests are really integration tests; not unit tests.

By the sounds of it, for a particular input file type and chosen output type, you supply a set of parameters to the utility and those parameters change for each input/output combination. Assuming this is correct, then to properly test the method, your approach is a good one (for now). You'd need to create an appropriate output file for each combination, which would allow all paths through the method (and the resultant utility call) to be tested.

Where it's a less than ideal approach is with regards to the likelihood of the utility changing in the future. If the output file changes with a new version of the tool, your tests will fail simply because the tool changed; not because your code is flawed. Thus you have a brittle test. Also a new version must offer more attributes that better convert the files for your customers, but your tests won't necessarily pick these new features up.

Therefore you'll likely need a workflow treating the tests as suspect, regenerating the output files, and checking for attribute changes each time a new version of the utility is released. If you put that in place, then this test approach should suit you well.

1

Despite his own comment, I think the OP does want to test the underlying converter implicitly.

Why? A unit test for a wrapper is almost useless (except that it is very complex, but then it should be split up into smaller less complex parts). You can only test if you do the call to the underlying lib correctly, and the arguments are passed correctly. But you will recognize that within the first few manual tests.

What's really interesting is whether the lib behaves as expected, especially for corner cases (eg .abc does not define a param that is mandatory for .xyz). AND if the lib still has the same behavior, when upgrading to a newer version.

In the end your approach is good, just two notes:

  • try to test only a few standard cases (smoke tests), but many interesting special cases
  • I'd use another structure for the resource files (I think it's more useful but it's a matter of taste, though): resources / abc to xyz / missing params / input.abc, expected.xyz

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.