views:

45

answers:

3

I have the following code (which I've dumbed down for the question):

    public void HandleModeInit(int appMode){
        switch(appMode){
            Case 1:
                DoThis();
            Case 2:
                DoThat();
            Case 3:
                //no mode 3
            Case 4:
                DoSomethingElse();
            Case else:
                //do nothing
        }
    }

How would you unit test this method without turning it into an integration test (where you end up testing what DoThis(), DoThat(), and DoSomethingElse() are doing)? Since these method calls are made to methods within the same class as HandleModeInit(), how would you test this?

Although ideally, the method calls would be extracted out into another class, what if this move doesn't make any sense?

A: 

Instead of using a switch/case statement to switch appModes, you can create an interface called IAppMode (I'll assume that the caller could decide which object to instanciate that properly implements IAppMode since they're already passing in an int to represent the App Mode).

Your caller would pass in an IAppMode object, then your method could call the DoThis() method of IAppMode.

You could then create a dummy object that implements IAppMode and inject it in to the method during testing.

It makes your code easer (using design patterns can do that) and test-able.

Justin Niessner
in the non-dumbed down version, appMode is something that's kept track of throughout the application - so I can't change it. So what we essentially need to do is execute methods *because* of the appmode, not *on* the appmode (if that makes any sense).
MunkiPhD
A: 

I realize (especially with you noting it) that the code is dumbed down, but if you do not test what the called methods do in this case, what are you actually testing? The value of appMode?

How exactly to attack this is hard (if not impossible) to say without knowing some about what is happening inside the called methods. Are they making any outbound calls (to external services, database, whatever) that can be mocked? If so, that may be a path forward. If they contain only behaviour that is encapsulated in the class, perhaps they alter some property value that you can check when the call to HandleModeInit returns?

I am typically not a fan of altering the design of my code for the sole purpose of making it more testable, but one approach would be to separate the public interface of your class from the internals, and then defining the internal interface as, well, an interface. That way you open up for mocking the internals.

Fredrik Mörk
The DoThis(), DoThat(), and DoSomethingElse() method calls are a relatively deep rabbit hole. Internally they end up doing quite a bit. Depending on which one is called, it might be interfacing with a device, reading a file, and setting properties amongst other things. If we start testing what all is changed or set we turn it into an integration test, tightly coupling what's going on into the test since we'd be verifying at all the different layers. Essentially, is this something that can't be tested, or can be, but *should actually* be tested as an integration test?
MunkiPhD
I would probably take a shot at trying to place a cut at all places where the code does such external communication, wrapping that functionality in a way that it can be mocked (so, create device interfaces, file readers and so on in some factory where you can inject mocked versions of this functionality).
Fredrik Mörk
A: 

If you have control over what is passed into this method then I would pass in an interface that takes appmode as an argument which is an AppModeImplementationFactory. The purpose of this factory is to create AppModeImplementations. The factory would look as follows:

public class AppModeImplementationFactory: IAppModeFactory
{
   public IAppModeImplementation Create(int appMode)
   {
      // switch case goes here to create the appropriate instance
   }
}

When you pass in the factory this can be a mocked instance and you can verify that the create method is called. If you want to verify the instance type that is returned you would need to do that under a different test. This approach will fulfill your need of executing logic because of the app mode because the factory will return the implementation that you need based on the appMode.

Hope this helps

Michael Mann