For several months since I work with legacy java code, these are some of the things I deal with:
- 0% testing coverage.
- Huge functions in cases I even saw some with over 300 lines of code.
- Many private methods and static methods in cases.
- Very hard code.
In the beginning, I was very confused, it was difficult for me to use TDD as a legacy. After riding around for weeks and practicing my unit tests and bullying skills, my fear decreased and I feel a little more confident. I recently discovered a book called: working with heritage , I didn’t read it, I just looked at the table of contents, and I discovered something new for me, Stitches. Apparently, this is very important when working in the legacy.
I think these stitches can help me break the dependencies and make my code visible so that I can increase the reach of the code and make my unit test more accurate.
But I have many doubts:
- Can someone explain to me the difference between seam and layout?
- Do Seams, violate TDD rules for not touching production code before it has been tested?
- Could you show me a simple example comparing Seam and Mock?
Below I would like to insert the example that I did today when I tried to break the dependency in order to test the code and, finally, increase the test coverage. I would appreciate if you could comment a little if you see some errors?
Here is the source code at the beginning:
public class ABitOfLegacy { private String sampleTitle; String output; public void doSomeProcessing(HttpServletRequest request) { String [] values = request.getParameterValues(sampleTitle); if (values != null && values.length > 0) { output = sampleTitle + new Date().toString() + values[0]; } } }
If I just add a unit test, which calls this method and asserts this variable output, has a certain value after the call, then I would be wrong because I am not testing the module, I would do integration testing, So what I need to do is get rid of the dependency that I have in the parameter. To do so, I replace the parameter with an interface:
public class ABitOfLegacy { private String sampleTitle; String output; public void doSomeProcessing(ParameterSource request) { String [] values = request.getParameters(sampleTitle); if (values != null && values.length > 0) { output = sampleTitle + new Date().toString() + values[0]; } } }
Here's what the interface looks like:
public interface ParameterSource { String[] getParameters(String name); }
The next thing I do is create my own implementation of this interface, but I turn on the HttpServletRequest as a global variable and implement the interface method using the / s HttpServletRequest method:
public class HttpServletRequestParameterSource implements ParameterSource { private HttpServletRequest request; public HttpServletRequestParameterSource(HttpServletRequest request) { this.request = request; } public String[] getParameters(String name) { return request.getParameterValues(name); } }
Up to this point, I believe that all modifications to the production code were safe. Now I am creating Seam in my test suite. If I understood well, now I can safely change the behavior of the seam. Here is how I do it:
public class FakeParameterSource implements ParameterSource { public String[] values = {"ParamA","ParamB","ParamC"}; public String[] getParameters(String name) { return values; } }
And the last step would be to get support from Seam to test the original behavior of the method.
import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import code.ABitOfLegacyRefactored; import static org.hamcrest.Matchers.*; public class ABitOfLegacySpecification { private ABitOfLegacy aBitOfLegacy; private String EMPTY = null; @Before public void initialize() { aBitOfLegacy = new ABitOfLegacy(); } @Test public void the_output_gets_populated_when_the_request_is_not_empty () { FakeParameterSource fakeParameterSource = new FakeParameterSource(); aBitOfLegacy.doSomeProcessing(fakeParameterSource); assertThat(aBitOfLegacy.output,not(EMPTY)); } @Test(expected=NullPointerException.class) public void should_throw_an_exception_if_the_request_is_null () { aBitOfLegacy.doSomeProcessing(null); } }
This will give me 100% coverage. I appreciate your thoughts:
- Did I break the addiction correctly?
- Are unit tests missing?
- What can be done better?
- Is this example good enough to help me understand the difference between a seam and a layout?
- How could I scoff if I don't use a seam?