3

Let's say your company sells software that comes with customizable text, and your team's job is to customize it. The client engagement includes a contract in which the client specifies all of the text resources and where your team guarantees a complete test cycle.

Somewhere in the code there is logic that looks like this:

var response = proxy.Foo(request);
if (!response.IsOK)
{
    switch (response.ErrorCode)
    {
        case Constants.BadProfile:
            DisplayError(Resources.BadProfile);
            break;
        case Constants.BadRequest:
            DisplayError(Resources.BadRequest);
            break;
        default:
            DisplayError(Resources.GeneralError);  //Should never happen
            break;
    }
}

Based on the way the code is written, it is literally impossible to generate a data condition resulting in the third error. But the engineering team put it there just in case of a future code flaw or to guard against the situation where the client is out of date and the proxy throws a new and unfamiliar error.

That being said, the client has customized all three error message resources and QA wants to test all three of them.

Would it be reasonable for QA to insist on testing to ensure the general error resource is correct? How can they possibly produce that error?

Or is inclusion of the error message a mistake in the requirements, since it specifies a feature with no definable acceptance criteria?

Note: proxy in this example is a third party service, so it is not possible to rig it to throw an unexpected error. We could of course modify the client code to force the error, but then we would not be testing the code base that will end up in production.

Edit:

The "proxy" in this case isn't a WCF or REST client. It is a black box binary DLL. There is no separation of concerns, no dependency injection, and no convenient transport to intercept. There is nowhere to shim the result without effort so heroic it would undermine the credibility of the test and the expenditure of effort for such an edge case.

On the other hand, flat out removal of the backstop just to hit a QA 100% stat seems a bit Dystopian, i.e. contrary to common sense.

We could just "hide" the feature and not tell the customer about it, but the text contains valuable support information that is specific to each customer.

5
  • 2
    When proxy is a third party service, can you replace it by some mock service, maybe by configuration? That would be a seem for testing. If not, I think "mistake in the requirements" describes it well.
    – Doc Brown
    Commented Dec 5, 2018 at 5:31
  • Please clarify. Is it impossible to get a third kind of error because the proxy just doesn't issue any? Then you can mock the proxy and substitute one that does issue it. Or is it because Constants has only these two values? Then maybe the question should be titled "How do I test unreachable code?" Commented Dec 5, 2018 at 7:19
  • @KilianFoth Great suggestion for the title, that is exactly what I was trying to spit out.
    – John Wu
    Commented Dec 5, 2018 at 9:29
  • While I would try to mock that request (I'm quite stubborn), looks like the cost of testing 1/3 of a conditional block won't provide you with a noticeable benefit. Have you considered a test double? What kind of service is "Foo". What protocol does it implement?
    – Laiv
    Commented Dec 5, 2018 at 9:43
  • 2
    The protocol is COM+. Its security mechanism leverages COM+ security's deepest, darkest secrets. Attempting to mock it would unleash chaos in our environments.
    – John Wu
    Commented Dec 5, 2018 at 9:50

3 Answers 3

6

Seriously, mock that proxy. It is a boundary of the system you are testing.

Either create:

  • A Full Stub that replaces the third party system entirely. Make it smart enough to handle a request in different ways, so that you can explore all options.
  • A Spy that sits between your code, and the third-party code. Make it smart enough to conditionally pass through requests, and conditionally return a different response. It should be capable of logging interactions. This allows all options to be explored, but also providing a window to see how your application integrates.

If you cannot do this yourself, push it back onto the engineering team. They can write a boundary that gives you test coverage. It will additionally make their own system easier to test via unit tests.

If that proxy is a representation of some well defined cross-process communication protocol, then you are lucky, because there are many off the shelf spies/stubs.

If it happens to be provided by a third-party library/non-standard protocol then the developers are going to need to use their languages abstractions mechanisms to provide facade/adapters and a way to use the stub/spy version instead of the third-party library option.

1

If the only thing you want is to test if all those error messages are correctly customized, there is a way more efficient solution to this than to invest months of work to create test scenarios for each and every error situation, just to make the application show the customized text by a "real" error. Instead,

  • ask the engineering team to provide some additional, simple, "test customize error texts" feature which allows to display each available error message from the resources on a dialog or form of similar size and style as the dialog or form where it would be displayed regularly, in case an error occurs.
  • ask them to build the feature in a way so it will also allow to switch quickly between the original text and the customized text for comparison purposes.

That will avoid any necessity to change any code like the snippet from your question, it will avoid the necessity to mock out that proxy, and it will make it easier by an order of magnitude for you (as well as for QA) to test all customizations. And, as a bonus, it will become trivial to test the customization of error texts which would otherwise show up only in (almost) unreachable code.

0

How to test Switch case? The dispatch can be done by objects.

switch (response.ErrorCode)
{
    case Constants.BadProfile:
        DisplayError(Resources.BadProfile);
        break;
    case Constants.BadRequest:
        DisplayError(Resources.BadRequest);
        break;
    default:
        DisplayError(Resources.GeneralError);  //Should never happen
        break;
}
  • Refactor Switch case to dictionary/mapping and vary errorCodeToMessage

    DisplayError(errorCodeToMessage.getDefault(response.ErrorCode, Resources.GeneralError))
    
  • Refactor mapping on equality to asking the object. Then, you can set the error code before that.

    response.getErrorResourceId()
    
  • I do not like if low-level logic calling the GUI. There can be so many abstractions and thoughts in between which I like to have written explicitly.

Thinking, my perfect code would be:

var response = proxy.Foo(request);
if (response.isOK()) {
    ...
} else {
    displayResponseError(response)
}

void displayResponseError() {
    errorCode = response.errorCode
    message = getMessageForResponseErrorCode(errorCode)
    displayErrorMessage(mesage)
}

getMessageForResponseErrorCode(errorCode) {
   return getMappingFromErrorCodeToMessage().get(errorCode, getDefaultErrorMessage())
}

This code has four methods to test. They could all be in the response if possible.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.