Showing posts with label unit testing. Show all posts
Showing posts with label unit testing. Show all posts

Monday, May 10, 2010

Job Hunt and a Coding Test in Java and Ruby

Thursday evening last week I had a Skype interview with the technical recruiter from a small New York City company, Cyrus Innovation.

One of the things we talked about was if I was interested in sticking with Java or if I'd be flexible about other technology, Ruby, in particular. I said I was open to other technology, but mainly focusing on Java because as it's what I'm doing day-to-day, it's what I'm most comfortable interviewing about.

At the end he said he would send me the Ruby coding test. I said something like "sure, I'll try that. Doing it in Ruby should give me a push to learn a little more Ruby". Then he apologized and said it was a slip of the tongue and he would send me the Java test.

Shortly after that I got an email from him with a link to their Java based coding test on their website. I read through it, and since he'd mentioned a Ruby test, thought I'd just tack on "_ruby" to the URL. That worked and I got a Ruby version...

So, Friday night I began work on the Java one, wrapped it up on Saturday evening and then decided to try it out in Ruby. It was essentially the same test, although the text on the website was formatted a little different and it gave the individual input files and output files instead of a zip containing them all, but really, the same test.

I worked from late Saturday night till nearly dawn on Sunday, searching Google for some Ruby help and digging in the index of The Ruby Programming Language by Flanagan & Matsumoto to find answers to my questions about getting it done in Ruby. I actually had a lot of fun doing that. With a dearth of ideas for what to program, rather than a lack of interest in learning, this gave me a specific project to do.

I continued it later Sunday morning and by early evening had the same output from the Ruby code as I did the Java code, and the same unit tests. I didn't copy and translate the code directly, I tried to use Ruby idioms where I understood them, but since it was my first real Ruby code, I doubt it's top quality.

I emailed both zip files to Cyrus Innovation with the explanation that he gave me the Java test and mentioned the Ruby one, so after finding I did it just so I could learn a little more Ruby. Hopefully that'll make some kind of good impression and back up what I said about being interested in learning (which is certainly true!).

So that's step 2 of 4 from their recruitment process done. Everything he told me about the company made it sound like a place I'd enjoy working. At this point I really want to get through the next steps and I hope they hire me, it sounds fantastic.

The only problem is step 4 of the process... I can't easily fly there to spend a day in their office and fly back. He said he'd talk to some people there about what might be done via the internet for that.

To Mock or not to Mock, that is the question...

Yoda: … But beware the mock side… Easy they flow, quick to join you in a test. If once you start down the mock path, forever will it dominate your destiny, consume you it will...
Luke: … Is the mock side stronger?
Yoda: No, no, no. Quicker, easier, more seductive.
Luke: But how am I to know the good mocks from the bad?
Yoda: You will know…

Over the last three years that I've been on a project doing extensive unit testing (not that I was ever doing unit testing before, though I'd heard the name) one of the major battles our team has had is about using mocks. Primarily it's "do you mock concrete classes or not". Mocking interfaces isn't such a question because that's the whole point of an interface, you write whatever implementation you want, and in a unit test that could very well be a mock object.

Mocking collaborators is a great way to work out the interactions between objects, and for stateless service methods allows you to isolate your tests to primarily just the object you're trying to test without by not forcing you to set up way too much data and classes and state around the other objects.

And of course, you have to think about the purpose of testing. Some say it's about using tests to design your objects. I don't disagree, but based on pretty long experience writing software, I think another purpose of tests is to "protect" your code from future changes.

We all know that code changes as the product/project or whatever evolves and goes through future changes, that's pretty much a given. Having spent years working on untested code, I like the idea that tests tell you later when changes affect code by failing. They tell you that a future change, possible to some other class, requires you to change a class you may not have thought of. It's great, and it helps reduce regression bugs later on.

At first it wasn't a question, we used JMock version 1 and that only allowed mocking interfaces. Using it we had test code that looked like:
Mock mockService = new Mock(BlahBlahBlahService.class);
mock.expects(once()).method("doSomething").with(eq(domainObjectId), eq(newValueAmount));

And so forth. It works, but the part that's inelegant is having the method name in quotes so that refactors don't automatically change it and you can't easily find where that method is called.

But then with JMock2, we got better syntax like:
BlahBlahBlahService mockService = createMock(BlahBlahBlahService.class);
addExpectations(new Expectations() { {
one(mockService).doSomething(domainObjectId, newValueAmount);
} });

But with this JMock2 also brought the ability to mock concrete classes... And that's where I think the trouble begins.

On our project we've set up "builders" for just about every domain object so that in tests we can fairly easily create a stateful domain object with any state our tests need. It works quite slick in our test code:
DomainObject domainObject = new DomainObjectBuilder()
.someStringValue("xyz")
.someOtherStringValue("abc")
.someObjectValue(new Object())
.someIntegerValue(123)
.build();

Granted, some can get fairly complicated with a test requiring building multiple objects. I'm not saying it's always 100% easy. And often if setting up data for your unit test is really hard, it's a sign your design isn't quite right and it's time to think about changing that. On the other hand, I kind of think that if the code is complicated, the test should be equally so, they go hand in hand, and if the test is too much simpler, it may not be a good test.

When we switched to JMock2, a lot of the younger, less experienced guys, who didn't have much experience at maintaining older code through generations of changes wanted to start mocking every object except the one being tested. They were concerned about "purity" and that every unit test had to be a "pure unit test" which meant that the object being tested was the only one not a mock. They wanted all other objects mocked so that each test tested only one single object.

I understand the argument about purity, but I disagree, if they're too pure, they're not future-proofing your code (in metallurgy, pure steel isn't as flexible if impurities, such as manganese aren't added). By mocking everything else, it prevents our tests from catching necessary changes in an object if the objects it collaborates with change in some way. The test is essentially "putting words in the mouth" of other objects. I think if an object is dependent upon another object, it's good for the test to be equally dependent.

Another coworker gave a good argument against "purity". How would you test this function?

public String truncateTo10Chars(String source) {
if (source.length() > 12) {
return source.substring(1, 12);
} else {
return source;
}
}

A mock test for that would be simply silly, mocking a String? It would look like:

String mockString = createMock(String.class);
String truncatedString = "123456789012";

addExpectations(new Expectations() { {
one(mockString).length()
will(returnValue(15));
one(mockString).substring(with(equalTo(1)), with(equalTo(12)));
will(returnValue(truncatedString));
} });

String actualString = truncateTo10Chars(mockString);
assertThat(actualString, is(truncatedString));

That'd be a bit of a crazy test...

One of the unit tests that a mock fan wrote in our code dealt with a domain object's method returning a particular enum value. The mock fans didn't want to build it, they wanted the test to be pure and the test of the class calling the domain object to mock the domain object to avoid "testing" the domain object itself. I tried to suggest "well, what if in the future it returns a different one of the enum's values, we'll have to change the code calling it because it's dependent upon what's returned, but the test won't tell us because it's still the same data type and it won't fail."

I lost the argument, not by logic but by not talking loud enough, not yelling my answer to "that'll never happen, this is the way we've written," and then the other mock fellows switching to Hindi to cut me out of it.

And two years later, we added a new option to the enum and changed the return values of newly created domain objects that used it. All the tests passed, and the code failed when we ran the application, because the tests used mocks and didn't tell us what code depended upon that domain object that needed changing.

Anyway, after quite a while I began to think a little different, and come to a balance. I saw an example of a concrete class where we didn't care what values were in it, we only wanted to make sure a particular method was called, and there a mock made sense.

So, what I've come to is, mocking interfaces is fine. But with concrete classes, never mock the state of it, but if your test has nothing to do with the state, it's okay. Essentially, that's mocking just the interaction, and that's essentially the concrete class's "interface".