【问题标题】:Mocking simple property access with JMockIt使用 JMockIt 模拟简单的属性访问
【发布时间】:2014-07-16 05:00:05
【问题描述】:

我有一个简单的 get-set 界面:

public interface Foo {

    void setBaz(String baz);
    String getProcessedBaz();    
}


这个接口是我实际测试类的依赖项。我正在尝试模拟 Foo 以获得这种有效的行为:

public class MockedFoo implements Foo {

    private String bazField;

    @Override
    public void setBaz(String baz) {
        bazField = baz;
    }

    @Override
    public String getProcessedBaz() {
        return "PROCESSED_" + bazField;
    }
}


所以我的预期结果是:

mockedFoo.setBaz("ABC");
assertEquals("PROCESSED_ABC", mockedFoo.getProcessedBaz());


我能够在Verification 中使用withCapture 捕获方法参数,但是如何设置具有相同输入值的Expectation?看来你可以做一个或另一个。

有没有办法在 JMockIt 中表达这一点?我使用的是最新版本 (1.9)。


注意:我知道我可以简单地设置一个Mockup<Foo> 实例并输入上面的所有代码。但是,我的真实代码要复杂得多,我不想手工制作整个模拟类。

【问题讨论】:

    标签: java unit-testing properties mocking jmockit


    【解决方案1】:

    您可以使用委托来执行此操作。你可以试试这个

    要测试的类

    public interface Foo {
    
        void setBaz(String baz);
        String getProcessedBaz();    
    }
    
    class FooSubClass implements Foo {
    
        private String bazField;
    
        @Override
        public void setBaz(String baz) {
            bazField = null;
        }
    
        @Override
        public String getProcessedBaz() {
            return bazField;
        }
    }
    

    测试类

    import mockit.Capturing;
    import mockit.Deencapsulation;
    import mockit.Delegate;
    import mockit.NonStrictExpectations;
    
    import org.junit.Before;
    import org.junit.Test;
    
    
    public class FooTest 
    {
        FooSubClass fooSubClass;
        @Capturing Foo fooMocked;
        @Before
        public void setUp()
        {
            fooSubClass = new FooSubClass();
        }
        @Test
        public void testAMethod()
        {
            new NonStrictExpectations()
            {
                {
                    fooMocked.setBaz(anyString);
                    result = new Delegate()
                            {
                        void setBaz(String baz)
                        {
                            Deencapsulation.setField(fooSubClass, "bazField", baz);
                        }
                    };
                    times = 1;
    
                    fooMocked.getProcessedBaz();
                    result = new Delegate()
                    {
                        String getProcessedBaz()
                        {
                            return "PROCESSED_" + Deencapsulation.getField(fooSubClass, "bazField");
                        }
                    };
                    times = 1;
                }
            };
            fooSubClass.setBaz("abc");
            System.out.println(fooSubClass.getProcessedBaz());
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      注意:这是受 Varun 回答的启发,但我想避免使用反射和中间类。 Rogério 还提供了一个可行的替代方案,但它不适合我的测试的整体结构。感谢两位!


      这是我最终如何让它工作的:
      public interface Foo {
          void setBaz(String baz);
          String getProcessedBaz();
      }
      
      @RunWith(JMockit.class)
      public class FooTest {
          @Injectable
          private Foo mockedFoo = null;
      
          @Test
          public void testBaz() { 
              new Expectations() {
                  private String bazState; // Variable inside Expectations stores the state between calls
      
                  {
                      mockedFoo.setBaz(anyString);
                      result = new Delegate() {
                          void setBaz(String baz) { bazState = baz; }
                      };
      
                      mockedFoo.getProcessedBaz();
                      result = new Delegate() {
                          String getProcessedBaz() { return "PROCESSED_" + bazState; }
                      };
                  }
              };
      
              mockedFoo.setBaz("ABC");
              assertEquals("PROCESSED_ABC", mockedFoo.getProcessedBaz());
          }
      }
      

      【讨论】:

        【解决方案3】:

        编写这种基于状态的测试的一种方法是:

        public interface Foo {
            void setBaz(String baz);
            String getProcessedBaz();
            void someOtherMethod();
        }
        
        public static class ClassUnderTest {
            String doSomething(Foo foo) {
                foo.setBaz("ABC");
                foo.someOtherMethod();
                return foo.getProcessedBaz();
            }
        }
        
        @Test
        public void mockFoo() {
            Foo foo = new MockUp<Foo>() {
                String baz;
                @Mock void setBaz(String baz) { this.baz = baz; }
                @Mock String getProcessedBaz() { return "PROCESSED_" + baz; }
            }.getMockInstance();
        
            String res = new ClassUnderTest().doSomething(foo);
        
            assertEquals("PROCESSED_ABC", res);
        }
        

        也可以使用 JMockit Expectations API(使用 Delegate 对象)编写等效的测试,但它会更加冗长,因为该 API 用于基于行为的测试(即,当你更关心调用了哪些方法而不是对象之间的状态转移)。

        【讨论】:

        • 谢谢@Rogério。我今天实际上正在使用Mockup&lt;T&gt;(见我问题的结尾)。与显式创建模拟类相比,这种方法似乎没有任何优势。我正在寻找一种(希望)更紧凑的方法。 :)
        • 那些不需要模拟的方法呢,比如上面例子中的someOtherMethod?这是一个优势,我相信。也就是说,我同意对于具有少量方法的接口,创建常规实现类更简单。
        • 我明白了,是的,这绝对是一个优势。
        • 我希望用更简单的语法得到更多的答案。如果没有,我会选择MockUp&lt;T&gt;
        猜你喜欢
        • 2015-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-19
        • 1970-01-01
        相关资源
        最近更新 更多