Disclaimer, I am an iOS developer that has been playing around with encryption on Android. As it stands I've managed to achieve encryption in Android but I'm asking myself how would one unit test for encryption and decryption of data?
Now the first idea that comes to mind would be something like:
String encryptedInputData = encryptedInputData("Hello");
String decryptedData = decryptData(encryptedInputData);
Assert.assertEquals(decryptedData,"Hello");
This test however poses one flaw... If something did change in the encryptedInputData
and decryptData
methods, this test would not tells what changed and why it is now breaking. So I would like to write far more granular tests. So for example given this code:
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] data = cipher.doFinal(message);
I'd like to make sure that the cipher
variable is using the RSA algorithm in ECB mode with no padding. I'd like to test that the message
in the .doFinal(message)
follows a particular format etc.
Now I would imagine I would be able to mock the Cipher
class, the problem here is that the encryption and decryption that was written, serves only as a Util class and to be able to unit test this, I would have to pass the mock Cipher
into the code, which given that this is a Util class seems like it would get messy i.e. I would have to either create an init method just for unit testing purposes or create setter methods just to unit test this. Which would allow me to unit test the code but then the Util class gets clunky with code that I actually don't need for production purposes.
Are there any elegant ways of being able to unit test scenarios like this? i.e. encryptedInputData
and decryptData
are public methods but these methods use various private methods which frankly need to be unit tested, the issue then is how?
As far as I can see
Cipher
is injavax.crypto
class. So you don't need to mock it.You can write unit tests to verify that: 1.
Key
is properly generated and does match requirements. 2.CipherInputStream
is successfully created. 3. Copying text to file and reading it after decryption does match:Although that test doesn't have explicit Assertion check, you can add:
and then check arrays:
Keep in mind that you should not compare bytes arrays by converting them to string, since it will give you wrong implementation.
The real answer is you shouldn't have to. You should never implement your own encryption routines. Not only are you very likely to get it wrong, there are extremely complex things you need to do to make sure it actually isn't hackable due to implementation issues (for example, if one branch of an if statement takes longer to run than another, you can figure out what the value of the check is). You should always use an open source, well reviewed library.
Since you aren't implementing it yourself, you don't need to unit test it. The library writers should be. If you feel like it, run their test suite as part of your own, but I'd consider that a waste of time- they did it before release, and you really only need to run it once at most.
Gabe is correct in his answer about being really really being conservative when re-inventing the wheel; especially when that wheel is about cryptography/security/... such things. Chances are: you will get it wrong. And hackers love people trying to come with their own "secure cryptography".
But to answer you actual question: try go with with TDD (test driven development). You see, your problem is that you created some APIs that are hard to test.
You are correct, you want that "end to end" like test that makes sure that decrypt(encrypt("something")) comes with with "something". Now your problem is: if you only write your code with those two methods in mind; then unit testing is hard.
Thus: right from the beginning, when you consider "which classes do I need"; and "which methods go into which class" you have to focus on "and how do I test that".
In other words: this is one of the occasions where TDD is super important - because you really want to design units that can be tested. And the best practice to get there is: write your tests first. If you design your units so that they can be tested (because you wrote the tests first); surprise - they can be tested. Making them "testable" after the fact is always cumbersome; and most often: close to impossible.