views:

495

answers:

3

I am looking to obfuscate our Java web app code within our existing Ant build script, but am running into problems around unit testing. I am obfuscating the code right after it has been compiled, before it is jar-ed and before the unit tests are ran.

However, if I obfuscate my production code and not my test code, all my tests fail because they are trying to call methods that no longer exist because they have been renamed by the obfuscator. I can mark certain methods to not obfuscate so they can be used by external systems such as our test suite, but since we are shooting for high unit test coverage we will need to mark all of our methods as un-obfuscatable.

If I obfuscate the test classes as well, I run into two problems:

1: The production classes and the test classes get merged into the same output directory and I am unable to exclude the test classes from the production .jar files

2: I cannot run my normal Ant batchtest call:

 <batchtest todir="${basedir}/reports">
      <fileset dir="${basedir}/components/common/build-zkm">
           <include name="**/*Test.class"/>
      </fileset>
 </batchtest>

because the obfuscator has changed the names of the tests.

I could just run the obfuscator on the resulting .war/.ear files, but I want to have our unit tests run against the modified code to drive out any bugs caused by the obfuscator.

I am currently working with Zelix KlassMaster, but I am still in the evaluation phase so I would be open to other options if they would work better.

+2  A: 

Can you tell it to run the obfuscator such that it effectively refactors the code including the references from the tests (i.e. when a production name changes, the test code changes its reference) but not to obfuscate the tests themselves (i.e. don't change the names of the test classes or their methods)? Given previous experience with obfuscators I'd expect that to work.

So for example, suppose we had unobfuscated source of:

public class ProductionCode
{
    public void productionMethod() {}
}

public class ProductionCodeTest
{
    public void testProductionMethod()
    {
        new ProductionCode().productionMethod();
    }
}

You want to set the options of the obfuscator to make it effectively:

public class Xyzzy
{
    public void ababa() {}
}

public class ProductionCodeTest
{
    public void testProductionMethod()
    {
        new Xyzzy(). ababa();
    }
}

That way your "run the tests" Ant tasks should be able to stay the same, because the API of the tests hasn't changed - merely the implementation of the methods.

Jon Skeet
I did find a way in KlassMaster to do this by adding "exclude *.*Test;" in my script and they do now run. Since I still have to run the tests through the obfuscator, they are still ending up mixed in with the production classes in the output directory. I'm checking for config options for that
Nathan Voxland
A: 

The obfuscator should not change your public calls. It seems that you do should run the other tests before obfuscation because they check internal functionality that should not change after obfuscation. So if that is the case why not just run the tests that call public functionality? All you need to do is have a saparate class with those calls and re-build it using the obfuscated code and then run that dll.

Dror Helper
While we have "public" methods, we don't have externally facing methods. I think that we should obfuscate internal-only public methods just like we would private methods. I would like to test everything post-obfuscation because there are bugs that it can introduce, especially around reflection.
Nathan Voxland
@Nathan Voxland I believe that that functionality should be tested via the public API as well - in other words it doesn't matter how you do it just that it works
Dror Helper
+5  A: 

I use yguard (it is free, which is why I mention it).

You should be able to tell the obfuscator not to obfuscate certain things (looking here it seems you can).

Some as others have said, don't obfuscate the tests, but do obfuscate the rest.

However, I would suggest that you do the following:

1) compile 2) jar the un-obfuscated files (if desired) 3) test the un-obfuscated files 4) if they pass the tests then obfuscate and jar the obfuscated files 5) test the obfuscated files

It will be slower, but if the tests fail in step 3 it'll be easier to fix (potentially) and if the tests fail at 5 then you know there is an issue with the obfuscation not your source code.

TofuBeer
Good point with the two-pass testing.
Nathan Voxland
Thanks for the yGuard link.
Liran Orevi