DaedTech

Stories about Software

By

JUnit for C# Developers 6 – Cart Before the Horse

In this post, I’d like to point out something I learned while working following yesterday’s post. In my haste to find the JUnit equivalent of MS Moles, I didn’t stop to think about what I was doing.

So, as I expanded on yesterday’s effort, I realized that my mocking of Runtime.getRuntime() didn’t seem to be working properly. As I set about trying to fix this, something dawned on me. Runtime.getRuntime() returns a Runtime object, and it’s that object’s exec(string) method that I’m interested in. So, in the code that I was trying to test, I was engaging in a double whammy of a Law of Demeter violation and inlining a static dependency.

I believe I was distracted, as I mentioned, by my desire to find C# equivalents in Java and by the general newness of what I was doing. But, this is a “teachable moment” for me. It’s easy to slip into bad habits when things are unfamiliar. It’s also easy to justify doing so. When I realized what I was doing, my first thought was “well, give yourself a break, Erik — just go with it this way until you’re more comfortable.” I then shook off that silly thought and resolved to do things right.

It’s easy to follow good design principles when you’re following a tutorial or being taught. But, it’s imperative to do it all the time so that it becomes a reflex. This includes when you’re tired late at night and just wanting to turn off your downstairs light without going downstairs (my situation now). It includes when you’re behind schedule and under the gun on a project. It includes when people are giving you a hard time. It’s always. If you practice doing it right — make it rote to do it right — then that’s what you’ll do by default.

So, humbled, here is my updated code:

Tests


@RunWith(Enclosed.class)
public class LightManipulationServiceHeyuImplTest {

	private static LightManipulationServiceHeyuImpl BuildTarget() {
		return BuildTarget(Mockito.mock(Runtime.class));
	}
	
	private static LightManipulationServiceHeyuImpl BuildTarget(Runtime runtime) {
		return new LightManipulationServiceHeyuImpl(runtime);
	}
	
	public static class constructor {
		
		/**
		 * This class makes no sense without a runtime, so don't let that happen
		 */
		@Test(expected=IllegalArgumentException.class)
		public void throws_IllegalArgumentException_when_runtime_is_null() {
			new LightManipulationServiceHeyuImpl(null);
		}
	}
	
	public static class toggleLight {
		
		/**
		 * Make sure the service is invoking the runtime's exec() to invoke heyu
		 * @throws IOException
		 */
		@Test
		public void invokes_getRuntimes_exec() throws IOException {
			
			Runtime myMock = PowerMockito.mock(Runtime.class);
			Process myProcessMock = PowerMockito.mock(Process.class);
            Mockito.when(myMock.exec(Mockito.anyString())).thenReturn(myProcessMock);
            
			LightManipulationServiceHeyuImpl myService = BuildTarget(myMock);
			myService.toggleLight(new Light("asdf", "Fdsa"), true);
			
			Mockito.verify(myMock).exec(Mockito.anyString());
		}
		
		/**
		 * If the exec works fine, then return true for successful command
		 * @throws IOException
		 */
		@Test
		public void returns_true_when_exec_does_not_throw() throws IOException {
			Runtime myMock = PowerMockito.mock(Runtime.class);
			Process myProcessMock = PowerMockito.mock(Process.class);
            Mockito.when(myMock.exec(Mockito.anyString())).thenReturn(myProcessMock);
            
			LightManipulationServiceHeyuImpl myService = BuildTarget(myMock);
			assertEquals(true, myService.toggleLight(new Light("asdf", "Fdsa"), true));
		}
		
		/**
		 * If the runtime's exec throws an exception, then this was not a successful op
		 * @throws IOException 
		 */
		@Test
		public void returns_false_When_exec_throws_exception() throws IOException {
			Runtime myMock = PowerMockito.mock(Runtime.class);
            Mockito.when(myMock.exec(Mockito.anyString())).thenThrow(new IOException());
            
			LightManipulationServiceHeyuImpl myService = BuildTarget(myMock);
			assertEquals(false, myService.toggleLight(new Light("asdf", "Fdsa"), true));
		}
		
		/**
		 * If we get a null process back, something went wrong
		 * @throws IOException
		 */
		@Test
		public void returns_false_when_exec_returns_null() throws IOException {
			Runtime myMock = PowerMockito.mock(Runtime.class);
            Mockito.when(myMock.exec(Mockito.anyString())).thenReturn(null);
            
			LightManipulationServiceHeyuImpl myService = BuildTarget(myMock);
			assertEquals(false, myService.toggleLight(new Light("asdf", "Fdsa"), true));
		}
	}
}

and class under test:

public class LightManipulationServiceHeyuImpl implements LightManipulationService {

	private Runtime _runtime;
	
	public LightManipulationServiceHeyuImpl(Runtime runtime) {
		if(runtime == null)
			throw new IllegalArgumentException("runtime");
		_runtime = runtime;
	}
	
	@Override
	public Boolean toggleLight(Light light, Boolean isOn) {
		try {
			return _runtime.exec("command") != null;
		} catch (IOException e) {
			return false;
		}
	}

	@Override
	public Boolean changeBrightness(Light light, int brightnessChange) {
		// TODO Auto-generated method stub
		return null;
	}
}

Obviously, I don’t want to execute the shell command “command”, but that’s tomorrow’s TDD. I’m happy for the evening, now that I’ve refactored an inline static Law of Demeter violation out of my design plans. 🙂

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
trackback

[…] var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(po, s); })(); Last time in this series, I posted about a good reminder of a couple of principles of good object oriented design […]