Java 在实现生成器模式时无法模拟

Java 在实现生成器模式时无法模拟,java,junit,mockito,builder,builder-pattern,Java,Junit,Mockito,Builder,Builder Pattern,我很难测试一个在逻辑中使用构建器模式(BuilderClass)的类(TestClass)。我无法模拟生成器类(BuilderClass)。下面是我的逻辑的简化版本 public class TestClass { public int methodA() { ExternalDependency e = BuilerClass.builder().withName("xyz").withNumber(10).build(); return e.callE

我很难测试一个在逻辑中使用构建器模式(BuilderClass)的类(TestClass)。我无法模拟生成器类(BuilderClass)。下面是我的逻辑的简化版本

public class TestClass {
    public int methodA() {
        ExternalDependency e = BuilerClass.builder().withName("xyz").withNumber(10).build();
        return e.callExternalFunction();
    }
}
这是我的建筑课

public class BuilderClass {
     public static BuilderClass builder() { return new BuilderClass(); }
     int number;
     String name;
     public BuilderClass withName(String name) {
         this.name = name;
         return this;
     }
     public BuilderClass withNumber(int number) {
          this.number = number;
           return this;
     }
     public ExternalDependency build() {
           return new ExternalDependency(name,number);
     }
 }
对于我的测试类,我将Mockito与Dataprovider一起使用

 @RunWith(DataProviderRunner.class)
 class TestClassTest {
 @Mock private ExternalDependency e;
 @Mock private BuilderClass b;
 @InjectMocks private TestClass t;
 @Before public void setUp() { MockitoAnnotations.initMocks(this); }
 @Test public void testMethodA() {
    when(b.withName(any(String.class)).thenReturn(b); //This is not mocking
    when(b.withNumber(10)).thenReturn(b); //This is not mocking
    Assert.notNull(this.t.methodA()); //Control while execution is going to implementation of withName and withNumber, which should not happen right.
    }
如果我错过了什么,请帮助我。谢谢
}

与kryger在上面的评论中所说的类似,您可能需要进行如下重构:

在测试的类中,创建一个seam,用mock替换
e

public class TestClass {
    public int methodA() {
        ExternalDependency e = buildExternalDependency("xyz", 10);
        return e.callExternalFunction();
    }

    protected ExternalDependency buildExternalDependency(String name, int number) {
          return BuilerClass.builder().withName(name).withNumber(number).build();       
    }

}
在测试代码中,重写测试类以用模拟替换
e
,并验证对生成器的输入:

   @RunWith(DataProviderRunner.class)
     class TestClassTest {
     @Mock private ExternalDependency e;
     private TestClass t;
     @Before public void setUp() { 
         MockitoAnnotations.initMocks(this); 
         t = new TestClass() {
             @Override
             protected ExternalDependency buildExternalDependency(String name, int number) {
                // validate inputs:
                Assert.assertEquals(10, number);
                Assert.assertEquals("xyz", name);
                return e;  // provide the mock      
             }    
        }
     }

     @Test public void testMethodA() {
        // TODO: mock behavior of callExternalFunction() here

            Assert.notNull(this.t.methodA()); 
    }
}

您可能希望进一步进行重构,将buildExternalDependency()移动到另一个类中,该类可以被模拟并注入TestClass的构造函数中

这并没有达到您预期的效果,因为您正在模拟对模拟的
BuilderClass b
实例的调用,但是
methodA
实际上总是从
BuilderClass.builder()调用中接收一个新的、未模拟的
BuilderClass
实例
@InjectMocks
无效,因为
TestClass
没有可注入的依赖项。一个明显的建议是将
ExternalDependency
的实例化移到
methodA
之外,以允许在测试时注入模拟实例。