Unit-testing method invocation (template method)?

Is it possible to unit-test method invocation on the same class(in order and how many times they were invoked) ?

i.e. A Template Method like the one below:

    abstract class FooBarBaz {

       public abstract void foo();
       public abstract void bar();
       public abstract void baz();

       // I want to create a unit test for this method, in the following order
       public myTemplateMethod() {
          foo();  // i want to check foo first;
          bar();  // i want to check bar second;
          baz();  // i want to check baz third;
       }
    }

EDIT - Posted Mockito Code Implementation

import junit.framework.TestCase;
import org.mockito.Mockito;

public class FooBarBazTest extends TestCase {

   public void testMyTemplateMethodWithMockito() {
      FooBarBaz mocked = Mockito.mock(FooBarBaz.class);

      mocked.myTemplateMethod();

      Mockito.verify(mocked, Mockito.times(1)).foo();
      Mockito.verify(mocked, Mockito.times(1)).bar();
      Mockito.verify(mocked, Mockito.times(1)).baz();
   }
}

EDIT - Added Stack Trace

testMyTemplateMethodWithMockito(sample.FooBarBazTest)  Time elapsed: 0.326 sec  <<< FAILURE!
Wanted but not invoked:
fooBarBaz.foo();
-> at sample.FooBarBazTest.testMyTemplateMethodWithMockito(FooBarBazTest.java:14)

However, there were other interactions with this mock:
-> at sample.FooBarBazTest.testMyTemplateMethodWithMockito(FooBarBazTest.java:12)

    at sample.FooBarBazTest.testMyTemplateMethodWithMockito(FooBarBazTest.java:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at junit.framework.TestCase.runTest(TestCase.java:168)
    at junit.framework.TestCase.runBare(TestCase.java:134)
    at junit.framework.TestResult$1.protect(TestResult.java:110)
    at junit.framework.TestResult.runProtected(TestResult.java:128)
    at junit.framework.TestResult.run(TestResult.java:113)
    at junit.framework.TestCase.run(TestCase.java:124)
    at junit.framework.TestSuite.runTest(TestSuite.java:232)
    at junit.framework.TestSuite.run(TestSuite.java:227)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
    at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:59)
    at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:115)
    at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:102)
    at org.apache.maven.surefire.Surefire.run(Surefire.java:180)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:350)
    at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1021)

Answers


Using a mocking framework is fine, but if you don't want to rely upon an external dependency, simply create a test class that implements your abstract class. Have the abstract implementations of the foo, bar, baz method add the strings 'foo', 'bar', 'baz' to a shared list, then in your test method just assert they are in the list in the correct order.

EDIT -- Ive written the tests both with mockito and the non-mock way. They both pass--I hadn't used mockito before, I might want to try it....

import junit.framework.TestCase;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class FooBarBazTests extends TestCase {


    @Test
    public void testMyTemplateMethod() {
        List tracker = new ArrayList();
        SimpleFooBarBaz toTest = new SimpleFooBarBaz(tracker);

        toTest.myTemplateMethod();


        assertEquals("foo", tracker.get(0));

        // more assertions
    }


    @Test
    public void testMyTemplateMethodWithMockito() {
        FooBarBaz mocked = mock(FooBarBaz.class);

        mocked.myTemplateMethod();

        // times(1) is unnecessary, but explicit
        verify(mocked, times(1)).foo();
        verify(mocked, times(1)).bar();
        verify(mocked, times(1)).baz();
    }

    class SimpleFooBarBaz extends FooBarBaz {


        List tracker;

        SimpleFooBarBaz(List tracker) {
            this.tracker = tracker;
        }

        public void foo() {
            tracker.add("foo");
        }

        @Override
        public void bar() {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void baz() {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        // others there

    }

}

Take a look at Mockito it has this behavior built into it.

From their documentation: 6. Verification in order

 List firstMock = mock(List.class);
 List secondMock = mock(List.class);

 //using mocks
 firstMock.add("was called first");
 secondMock.add("was called second");

 //create inOrder object passing any mocks that need to be verified in order
 InOrder inOrder = inOrder(firstMock, secondMock);

 //following will make sure that firstMock was called before secondMock
 inOrder.verify(firstMock).add("was called first");
 inOrder.verify(secondMock).add("was called second");

And: 4. Verifying exact number of invocations / at least x / never

 //using mock 
 mockedList.add("once");

 mockedList.add("twice");
 mockedList.add("twice");

 mockedList.add("three times");
 mockedList.add("three times");
 mockedList.add("three times");

 //following two verifications work exactly the same - times(1) is used by default
 verify(mockedList).add("once");
 verify(mockedList, times(1)).add("once");

 //exact number of invocations verification
 verify(mockedList, times(2)).add("twice");
 verify(mockedList, times(3)).add("three times");

 //verification using never(). never() is an alias to times(0)
 verify(mockedList, never()).add("never happened");

 //verification using atLeast()/atMost()
 verify(mockedList, atLeastOnce()).add("three times");
 verify(mockedList, atLeast(2)).add("five times");
 verify(mockedList, atMost(5)).add("three times");

Need Your Help

Access-Control-Allow-Origin issue and angular $http service

javascript jquery angularjs

Sorry for duplicating a question for this issue but I really tried to seek for a solution but I could not find it. So I use angular $http.get xhr request to retrieve data from the public api. I hav...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.