I've been testing my code behavior using TestNG and JMockit for a while now and I have had no specific issue with their combination. Today I came across a situation where I needed to mock one of my internal dependencies, in the so called, type wide manner and I did not need to keep that mock around since none of the test cases dealt with it directly while they counted on the mocked version functionality. So, naturally, I put the mocking logic in my @BeforeMethod. Here is a sample:

public class SampleTest
{
    @Mocked
    @Cascading
    private InnerDependency dependency;

    @BeforeMethod
    public void beforeMethod()
    {
        new NonStrictExpectations()
        {
            {
                dependency.getOutputStream((String)any);
                result = new Delegate<OutputStream>()
                {
                    public OutputStream getOutputStream(String url)
                    {
                        return null;
                    }
                };
            }
        };
    }

    @Test
    public void testNormalOperation()
    {
        // The test whose desired behavior depends on dependency being mocked out
        // ..
    }
}

But, since my tests do not care about the mocked dependency explicitly, I'm not willing to declare it as a test class field, unlike what is done above. To my knowledge of JMockit The only options remaining would be:

  1. Declare dependency as a local mock field:

    new NonStrictExpectations()
    {
        @Cascading
        private InnerDependency dependency;
        {
            //...
        }
    }
    
  2. Declare dependency as an input argument for beforeMethod(), similar to what is done for normal @Test methods:

    @BeforeMethod
    public void beforeMethod(@Mocked @Cascading final InnerDependency dependency)
    {
       // ...
    }
    

I see that JMockit 1.6+ would not like the first option and warns with WARNING: Local mock field "dependency" should be moved to the test class or converted to a parameter of the test method. Hence, to keep everyone happy, I'm ruling this option out.

But for the second option, TestNG (currently 6.8.6) throws exception when running the test saying java.lang.IllegalArgumentException: wrong number of arguments. I don't see this behavior with normal @Test cases passed with @Mocked parameters. Even playing with @Parameter and @Optional will not help (and should not have!).

So, is there any way I could make this work without declaring the unneccessary test class mock field, or am I missing something here?

Thanks

有帮助吗?

解决方案

Only test methods (annotated with @Test in JUnit or TestNG) support mock parameters, so the only choice here is to declare a mock field at the test class level.

Even if not used in any test method, I think it's better than having it declared in a setup method (using @Before, @BeforeMethod, etc.). If it were to be possible, the mock would still have to apply to all tests, because of the nature of setup methods; having a mock field of the test class makes it clear what the scope of the mock is.

其他提示

Dynamic partial mocking is one more technique to specify @Mocked dependencies locally. However, it has it's limitations (see comments below).

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top