单元测试方法调用(模板方法)?

时间:2010-12-03 00:58:24

标签: java unit-testing mocking

是否可以在同一个类上进行单元测试方法调用(按顺序调用它们的次数和次数)?

即。模板方法如下:

    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;
       }
    }

编辑 - 发布Mockito代码实施

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();
   }
}

编辑 - 添加了堆栈跟踪

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)

2 个答案:

答案 0 :(得分:4)

看看Mockito它内置了这种行为。

从他们的文件:
6.按顺序验证

 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");

和:
4.验证确切的调用次数/至少x /从不

 //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");

答案 1 :(得分:2)

使用模拟框架很好,但如果您不想依赖外部依赖,只需创建一个实现抽象类的测试类。让foo,bar,baz方法的抽象实现将字符串'foo','bar','baz'添加到共享列表中,然后在测试方法中断言它们以正确的顺序存在于列表中。

编辑 - 我用mockito和非模拟方式编写了测试。他们都通过了 - 我之前没有使用过mockito,我可能想尝试一下......

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

    }

}