Hacker Newsnew | comments | show | ask | jobs | submit login
Testing Without Interfaces (rtigger.com)
27 points by RTigger 890 days ago | 7 comments



One of the threads I was following on twitter last week was an exchange between a Microsoft architect and a unit tester. The tester was complaining about the lack of interfaces around a lot of the .net classes, especially in new features. The Microsofty countered with the increased risk of people rolling their own implementation of critical functionality and the increased demand on support that would produce.

It's a poor excuse, in my opinion. We have the choice to treat software the same way that people treat hardware products. Give notice that you will not be supported if you do things that are outside intended use. With appliances, this is called 'voiding the warranty.'

The problem is that we have not developed a culture which accepts those sorts of agreements yet. The closest I've seen (and I've been tracking the testability issue for frameworks and libraries for years) is the Eclipse notion of soft-final.

They used comments to indicate that you should not subclass particular classes. They could have marked them final but they chose not to in order to support their internal development. But, the message was clear: "if you subclass this, and a future version breaks you, it is your problem not ours."

The fact of the matter is that language-based protection mechanisms are too coarse. They are an attempt to solve a social problem (what people are allowed to do) with technology and there's no way to get it right consistently. Better to leave it in the social sphere.

-----


I agree with this 100%. It's embodied in the "we're all consenting adults" philosophy of python: http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Obj...

Marking variables as private using naming conventions like foo._dont_mess_with_me is also often helpful. Tools like linters can warn you that you're doing the wrong thing (messing with object internals) but the language doesn't get in your way or force you to use things like reflection to do what you want.

-----


"Better to leave it in the social sphere." I don't consider this some idle philosophical stance, either. The technical solutions don't actually solve the problem, since people work around the attempt, and they create additional problems, such as the one at the heart of this thread. I still marvel at how common this combination is: the "solution" doesn't actually solve the problem and creates others that wouldn't otherwise exist.

-----


There should be a name for that generative pattern, eh?

-----


Trying to unit test the internet is not a good idea, your life will be easier if you refactor things and simplify a little.

  public bool StringIsEvenLength(string somestring )
  public string FetchRemoteString( string uri )

  public bool RemoteStringIsEvenLength(string uri)
  {
      fetched = FetchRemoteString( uri );
      if( fetched != null ) {
        return StringIsEvenLength( fetched );
      }
      throw new SomeOtherException("Didn't get a string to check the length of.");
  }
Now StringIsEvenLength is trivial to test. When you want to test RemoteStringIsEvenLength

You can mock out FetchRemoteString and don't even have to use a WebClient at all.

-----


That's a pretty good point, and a much better solution to the example I gave. It'd be interesting to see FetchRemoteString as a Func<string, string> property where you could have a default implementation (web client) and then in your test setup you could just redefine the property to do whatever you want.

The general point still stands though - if there was an interface, we wouldn't have to come up with these workarounds.

-----


Having started my professional career as a Tester (SDET) at Microsoft, this topic resonates with me. In my experience testers need to be more aggressive and assertive with the code their developers have written. Obviously this implies you've already established a good relationship with your coworkers, but you shouldn't be afraid to tell developers that their untestable code needs to be given a second look. It's not pleasant telling someone "their baby looks ugly," but that is one of the responsibilities of white box testers.

Dependency Injection greatly improves the landscape. Don't instantiate concrete classes when you can have a DI framework construct those objects for you. This will allow you as the tester to supply the mocked objects rather than taking the wrapper approach. It is better if as a team you've introduced this design decision up front, but you can introduce it later for problematic classes that need extra attention. In Java, I love using Guice for this sort of thing, in .NET I've used Ninject in the past.

One thing that .NET has in its favor that Java doesn't is partial classes with an internal access modifier. You can then put your test code in these partial classes and grant them access to your assembly. This allows you to separate your private test code hooks from your shipping code, and it prevents someone from deriving from your class and using those test interfaces incorrectly.

This line is the problem: public WebClient client = new WebClient(); client shouldn't be public and it shouldn't be using a concrete class. Introduce an interface, use DI, and use internal partial classes as needed. If your developer won't do this, find a new developer. Anyone can write code, but writing good testable code with a clean separation of intents is something some developers never learn, usually because no one has told them, "You're doing it wrong."

-----




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: