Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Tomek Kaczanowski - Practical Unit Testing with JUnit and Mockito - 2013.pdf
Скачиваний:
224
Добавлен:
07.03.2016
Размер:
6.59 Mб
Скачать

Chapter 7. Points of Controversy

And so we have a closed circle. Definitely, then, we will have to explore further options to find a way out of this situation.

Another approach you can take, even with the most vicious legacy code, is to refactor a little, test a little, and then refactor again, and test, and refactor, etc. moving in baby steps. You will probably need to start with integration tests (which are sometimes easier to write for legacy applications, which consists of many tightly coupled classes and external resources like databases) and then gradually make your tests more focused. Step by step… This is a tedious task, and by no means a short one. The benefit is that you end up with a loosely coupled, testable application. However, it is a potentially dangerous approach and, given the complexity of the code and its legacy nature, will not guarantee success.

Because of the difficulties involved with this, some people opt to take the "easy" path. They use techniques (and/or tools) which will allow them to test private methods as they are, without any additional work on the code structure (or with only a minimal amount of this).

To sum things up, let us make the following observations:

No one wants to promote private methods testing - but some of us believe that sometimes this is the only way.

Some developers demand that their code be tested and 100% object-oriented, while others believe that testing is enough and do not struggle to achieve clean design.

When writing new code, we are conveniently positioned to write it so that it is fully tested via its public API. TDD might help to achieve this.

When working with legacy code we compromise on private method testing. Since the code plays unfair, we also forget about fair-play.

7.5.3. Private Methods Testing - Techniques

Keeping in mind all the arguments against private methods testing, we should at least be prepared to test them. Sometimes it might just save our lives! Now we shall discuss the two most popular techniques for testing private methods. We will use the following class to demonstrate them:

Listing 7.9. Class with a private method

public class SomeClass {

private boolean privateMethod(Long param) { return true;

}

}

This is the method we would like to test.

Reflection

This technique uses a Method class from the java.lang.reflect package9, which allows one to gather information on methods, but also to tweak them – e.g. introducing changes to their access modifiers. An application of this class in test code is shown in Listing 7.10.

9See http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary.html

157

Chapter 7. Points of Controversy

Listing 7.10. Testing private methods using reflection

public class PrivateMethodReflectionTest {

@Test

public void testingPrivateMethodWithReflection()

throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

SomeClass sut = new SomeClass();

Class[] parameterTypes = new Class[1]; parameterTypes[0] = java.lang.Long.class; Method m = sut.getClass()

.getDeclaredMethod("privateMethod", parameterTypes); m.setAccessible(true);

Object[] parameters = new Object[1]; parameters[0] = 5569L;

Boolean result = (Boolean) m.invoke(sut, parameters);

assertTrue(result);

}

}

The SUT only contains a single private method, which is to be tested. Reflection is employed to set privateMethod() as accessible. invoke() returns Object, so we need to cast it to the expected type. Asserting that privateMethod() works as expected.

Obviously, this is not the sort of code we should be writing everyday! It is ugly, it uses magic (disguised as reflection), and it will break if you refactor privateMethod() to anything else. We could dispose of some of its weaknesses using a tool that will hide all this nasty code behind some nice API. This can be done, for example, using PowerMock’s org.powermock.reflect.Whitebox class. Listing 7.11 shows this.

Listing 7.11. Testing private methods using PowerMock

public class PrivateMethodPowermockTest {

@Test

public void testingPrivateMethodWithReflection() throws Exception {

SomeClass sut = new SomeClass();

Boolean result = Whitebox

.invokeMethod(sut, "privateMethod", 302483L);

assertTrue(result);

}

}

All reflection calls are hidden behind a convenient API. You do not even have to cast the result to an appropriate type.

Even though this code looks much nicer than that of the previous attempt, it still cannot be refactored safely. Calling methods using their name (String) as the parameter is not a healthy approach.

One more thing to note is that neither the approach involving direct use of reflection, nor that which makes use of Powermock, require us to modify the production code. This is a good thing.

158

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]