Site icon Accidentally in Code

#iOSDevUK: Jon Reid – Controlling Dependencies to Enable TDD

My notes from John Reid‘s talk at iOSDevUK.

Credit: DeviantArt / LoneWolfAssassin

Barriers to TDD. Two primary:

EBay Fashion app. All test driven.

3 Types of Unit test:

Patterns of testing. The Design Patterns book, the Gang of Four never intended it to be the beginning and end of design patterns.

Not going to be rocket since. About getting through the barrier. Writing unit tests after if necessary, but ideally before.

Return Value Test:

With this alone, you should be able to get a very far distance. Onboarding engineers at Facebook, teach them not to be shy about extracting stand alone functions. Helps overcome that barrier.

State test:

Since interested in a side effect, just need an additional call to verify state. Should be able to write quite a few tests with these two techniques.

Interaction Test:

Don’t need to be isolated units. They can be connected, as long as they are fast. Check that the system under test (SUT) is communicating correctly to something else.

Don’t want to talk to the real thing:

Want a fake thing that the test can control. Need dependency injection, if the middle thing is creating the end thing, it’s hard to test.

Dependency Injection:

Difference between having a singleton, and a single way to access a singleton. E.g. NSUserDefaults. Don’t want to access it in this way.

Extract and Override: read “Working Effectively with Legacy Code” (Amazon).

TDD was working for me in a greenfield project, but how many of us get to stay in such a place?

Seams

Make a cut – subclass, override “userDefaults”, do what you want. Very powerful. Very effective with legacy code. Very dangerous. Like a drug. But will end up with the bane of testing code, fragile tests, because tests are coupled to implementation.

For getting started, especially with legacy code – good technique.

Method Injection

Better for other things, like calling “[NSDate date]” – will cause havoc with tests. Can swizzle, or just pass in what time you want. Now you will have a method that does more, now it’s tied to any time, not the current time. Helpful as context for injected object is very small. When spans across method, probably want to hang on to it as a property.

Test can inject the fake thing. But what about production code? Can end up with nil. Objective C will be like “whatever”.

Create custom getter with lazy eval. If no value, get the default value.

Inject in constructor – workhorse of dependency injection. Biggest benefit, makes everything explicit.

Can be annoying to have everything explicit. Long chain of dependencies is a code smell – you have too many dependencies.

Even then, you can simplify that, by using a Builder. Builder pattern creates the object you want according to however it is set. Set in any order, or not set and have it have defaults.

Constructor injection is the main one.

Ambient Context. Change something globally. Swizzling is an example of this. You can, sometimes helpful. But dangerous. Have to have your test restore the pre-test condition.

Let’s learn some good things from other people in other disciplines. There are plenty of smart people who are not using Obj-C

Interaction Test

Types of Fakes: The Art of Unit Testing

Don’t need a DI framework in order to do DI as a concept.

Mocking, if never mocked before don’t use OCMock or OCMockito at first. Use them eventually. Meanwhile, you can make your own fake. Subclass and override all methods. Test Driven iOS development, means don’t have to do that in Obj-C. Dynamic language, supports DuckTyping.

Subclass NSObject. Put the method in. Use a simple property to record the number of calls. Have a fake return value (if unspecified is nil). Capture arguments.

Interesting thing about doing by hand, answers question of “what do we do in swift”. No introspection available to us. Do it by hand, laborious, might cry a little bit, but nothing stopping us.

Now we have a mock, use it. Start writing some tests.

Exit mobile version