DEV Community

Jan Van Ryswyck
Jan Van Ryswyck

Posted on • Originally published at principal-it.eu on

Indirect Inputs and Outputs

Previously we discussed what state and behaviour verification is about. Depending on the nature of the production code that we’re designing, we might decide to apply one approach over the other. Sometimes this can be quite the dilemma that we face as developers. Which approach should we choose and in which circumstance? Robert C. Martin, also known as Uncle Bob, has named this tension field the “Uncertainty Principle of TDD”.

In this blog post, we’re going to dive deeper into the realm of behaviour verification. More specifically, we’re goingto discuss indirect inputs and outputs.

When calling a method or a function that expects one or more arguments, we need to specify a value for these arguments. These are the (direct) inputs for that method or function. Likewise, a method or a function can also return a particular value. This is the (direct) output for that method or function.

Visualisation of a direct input and output

Only a very few classes in a system can stand on their own while fulfilling their promised functionality. Most classes interact with one or more collaborators, also called dependencies, in order to provide the necessary capabilities.

When a class calls a method provided by a collaborator, and this method returns a particular value, then this value serves as an indirect input for the calling method. Such a method being called on a collaborator is called a query. Using this kind of method, we can query the dependency for a particular value. Note that in a well modelled system, queries have no side effects.

Visualisation of indirect inputs

Likewise, when a class calls a method provided by a collaborator that doesn’t return any value, but only receives one or more arguments, then this invocation along with its arguments serves as an indirect output of the calling method. A method being called on a collaborator that provides no return value is called a command. Using such a method, we can instruct or “command” the dependency to do something for the caller. A command is most likely to have side effects.

Visualisation of indirect utputs

A method or a function that is either a query or a command is described by the CQS principle. This acronym stands for Command Query Separation. and was devised by Bertrand Meyer, the creator of the Eiffel programming language. This principle simply states that every method should either be a query that returns data to the caller or a command that performs an action, but not both. In other words, “Asking a question should not change the answer”.

This is an important concept for unit tests that are using the behaviour verification approach. The general guideline and best practice is that a unit test should never verify whether a particular query method of a collaborator is being called. Indirect inputs should be treated the same way as a direct input. For a direct input, a unit test just needs to specify the necessary arguments for invoking a method of the Subject Under Test. Similarly, all required indirect input values should also be specified through the test doubles that are put in place. This is usually set up during the Arrange stage of a unit test. Direct inputs should never be verified in the Assert stage of a unit test. Avoid setting expectations for query methods at all costs.

[Establish]
public void Context()
{
    HeadOfDepartment headOfDepartment = Example.HeadOfDepartment().WithId(ApproverId);

    _expenseSheet = Example.ExpenseSheet()
        .WithId(ExpenseSheetId)
        .WithExpense(36.50m, new DateTime(2018, 11, 01), "Sushi dinner");

    var approverRepository = Substitute.For<IApproverRepository>();
    approverRepository.Get(ApproverId).Returns(headOfDepartment);

    _expenseSheetRepository = Substitute.For<IExpenseSheetRepository>();
    _expenseSheetRepository.Get(ExpenseSheetId).Returns(_expenseSheet);

    _sut = new ApproveExpenseSheetHandler(_expenseSheetRepository, approverRepository);    
}

Here we’ve specified two indirect inputs. One being an instance of the HeadOfDepartment class. This object is returned by the Get method of the ApproverRepository test double. The other being an instance of the ExpenseSheet class. This object is returned by the Get method of the ExpenseSheetRepository test double.

The test doubles that we’ve just highlighted in our example are called “Stubs”. Stubs just return fake results to the Subject Under Test. We do not perform any expectation checks on them whatsoever.

When the Subject Under Test provides an indirect output to a collaborator by means of calling a command method, weneed to verify that this action has occurred. Indirect outputs should be treated the same way as a direct output. A direct output is verified by a unit test using an assert statement. Therefore an indirect output should also be verified. This verification is usually done during the Assert phase of a unit test by verifying an expectation on a test double. This is a slightly different way of asserting but should be treated the same way as a classic assert statement.

[Observation]
public void Then_the_approved_expense_sheet_should_be_saved()
{
    var lastReceivedCall = _expenseSheetRepository.ReceivedCalls().Last();
    Assert.That(lastReceivedCall, Is.Not.Null);
    Assert.That(lastReceivedCall.GetMethodInfo().Name, Is.EqualTo("Save"));

    var savedExpenseSheet = lastReceivedCall.GetArguments().First() as ExpenseSheet;
    Assert.That(savedExpenseSheet, Is.Not.Null);
    Assert.That(savedExpenseSheet, Is.SameAs(_expenseSheet));
}

Here we’ve made an observation whether the last called method on the ExpenseSheetRepository test double has beenthe Save method. We also ensured that the saved ExpenseSheet object is the one that we expect. If this is the case, then the test will pass. If not, then the test will fail.

The test double that we’ve just seen in our example is called a “Spy”. Spies are used to verify whether the collaboration between the Subject Under Test and the dependency has been carried out. A “Spy” is considered to spy on the relationship between the two parties.

Now that we’ve touched on the concepts of “Stubs” and “Spies”, in the next blog post we’ll discuss the other different types of test doubles that we have at our disposal.

Top comments (0)