Unit Testing - Is it bad form to have unit test calling other unit tests
Asked Answered
N

8

30

I have a unit test called TestMakeAValidCall(). It tests my phone app making a valid call.

I am about to write another test called TestShowCallMessage() that needs to have a valid call made for the test. Is it bad form to just call TestMakeAValidCall() in that test?

For reference this is my TestMakeAValidCall() test.

    [TestMethod]
    public void TestMakeAValidCall()
    {
       //Arrange
        phone.InCall = false;
        phone.CurrentNumber = "";
        // Stub the call to the database
        data.Expect(x => x.GetWhiteListData()).
            Return(FillTestObjects.GetSingleEntryWhiteList());
        // Get some bogus data
        string phoneNumber = FillTestObjects.GetSingleEntryWhiteList().
            First().PhoneNumber;
        // Stub th call to MakeCall() so that it looks as if a call was made.
        phone.Expect(x => x.MakeCall(phoneNumber)).
            WhenCalled(invocation =>
                       {
                           phone.CurrentNumber = phoneNumber;
                           phone.InCall = true;
                       });

       //Act
        // Select the phone number
        deviceControlForm.SelectedNumber = phoneNumber;
        // Press the call button to make a call.
        deviceMediator.CallButtonPressed();

       //Assert
        Assert.IsTrue(phone.InCall);
        Assert.IsTrue(phone.CurrentNumber == phoneNumber);
    }
Neolithic answered 2/9, 2009 at 16:51 Comment(1)
Thanks for all the great answers. I refactored out the duplicate code to a separate call. I picked the answer with the most votes.Neolithic
C
64

Refactor the setup to another method and call that method from both tests. Tests should not call other tests.

Crassus answered 2/9, 2009 at 16:53 Comment(5)
Dont test another test with testWalkup
Could someone ellaborate on how the refactoring would look like in this case?Forayer
@Nakata - hard to show the exact code for this example without knowing how the dependencies are structured, but this gist shows the pattern I use to put set up code in a test context and invoke it from there in multiple tests. gist.github.com/tvanfosson/1ca6dd3bb0b796de65b0Crassus
Thank you, is it a good convention to put the SetupCall-method in a nested class?Forayer
@Nakata - using the inner class restricts access to its public methods to the containing class. I do this to limit coupling between the test classes, removing the temptation to over-engineer them to meet the needs of unrelated test classes. If you truly have code that can be reused, move it to a base test context that is outside the test class(es) and extend the inner test contexts from that base class.Crassus
M
11

IMHO, you should do one of the following:

  • Create a method that returns a valid call, and use it separately for both tests (not one calling the other)
  • Mock the valid call for the ShowCallMessageTest
Mendive answered 2/9, 2009 at 16:54 Comment(0)
S
7

To offer a counter point:

I strongly believe that well designed unit test should depend on one another!

Of course, that makes sense only if the testing framework is aware of these dependencies such that it can stop running dependent test when a dependency fails. Even better, such a framework can pass the fixture from test to test, such that can build upon a growing and extending fixture instead of rebuilding it from scratch for each single test. Of course, caching is done to take care no side-effects are introduced when more than one test depends from the same example.

We implemented this idea in the JExample extension for JUnit. There is no C# port yet, though there are ports for Ruby and Smalltalk and ... the most recent release of PHPUnit picked up both our ideas: dependencies and fixture reuse.

PS: folks are also using it for Groovy.

Surpass answered 23/11, 2009 at 8:46 Comment(1)
Interesting point, if done correctly, it could reduce duplication a lot.Exaggeration
V
6

I think its a bad idea. You want your unit test to test one thing and one thing only. Instead of creating a call through your other test, mock out a call and pass it in as an argument.

Vow answered 2/9, 2009 at 16:54 Comment(0)
S
4

A unit test should test one unit/function of your code by definition. Having it call other unit tests makes it test more than one unit. I break it up in to individual tests.

Shakitashako answered 2/9, 2009 at 16:56 Comment(0)
R
2

Yes - unit tests should be separate and should aim to test only one thing (or at least a small number of closely-related things). As an aside, the calls to data.Expect and phone.Expect in your test method are creating expectations rather than stub calls, which can make your tests brittle if you refactor...

Redemptioner answered 2/9, 2009 at 16:56 Comment(1)
Thanks for the correction on my terminology. I have updated the comments in my Source Code.Neolithic
A
1

A unit vs. module....we also think tests should depend on reusable methods as well and should test at an api level testing integration of classes. Many just tests a single class but many bugs are at that integration between the class level. We also use verifydesign to guarantee the api does not depend on implementation. This allows you to refactor the whole component/module without touching a test(and we went through that once actually and it worked great). Of course, any architectural changes force you to refactor the tests but at least design changes in the module don't cause test refactor work(unless you change the behavior of the api of course implicitly like firing more events than you used to but that "would" be an api change anyways).

Argeliaargent answered 14/12, 2011 at 17:0 Comment(0)
J
0

"Could someone ellaborate on how the refactoring would look like in this case? – Philip Bergström Nov 28 '15 at 15:33"

I am currently doing something like this and this is what i came up with:

Notice that ProcessorType and BuildProcessors both call TestLevels

the actual content besides that fact is unimportant

its using XUnit, and Shouldly NuGet package

private static void TestLevels(ArgProcessor incomingProcessor)
{
    Action<ProcessorLevel, int> currentLevelIteration = null;
    currentLevelIteration = (currentProcessor, currentLevel) =>
    {
        currentProcessor.CurrentLevel.ShouldBeEquivalentTo(currentLevel);
        ProcessorLevel nextProcessor = currentProcessor.CurrentProcessor;
        if (nextProcessor != null)
            currentLevelIteration(nextProcessor, currentLevel + 1);
    };

    currentLevelIteration(incomingProcessor, 0);
}

[Theory]
[InlineData(typeof(Build), "Build")]
public void ProcessorType(Type ProcessorType, params string[] args)
{
    ArgProcessor newCLI = new OriWeb_CLI.ArgProcessor(args);

    IncomingArgumentsTests.TestLevels(newCLI);

    newCLI.CurrentProcessor.ShouldBeOfType(ProcessorType);
}

[Theory]
[InlineData(typeof(Build.TypeScript), "TypeScript")]
[InlineData(typeof(Build.CSharp), "CSharp")]
public void BuildProcessors(Type ProcessorType, params string[] args)
{
    List<string> newArgs = new List<string> {"Build"};
    foreach(string arg in args) newArgs.Add(arg);

    ArgProcessor newCLI = new OriWeb_CLI.ArgProcessor(newArgs.ToArray());

    IncomingArgumentsTests.TestLevels(newCLI);

    newCLI.CurrentProcessor.CurrentProcessor.ShouldBeOfType(ProcessorType);
}
Johnjohna answered 25/12, 2019 at 0:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.