使用Mockito测试SpringBoot应用程序时,如何从属性文件传递变量



解决方案

代替注释字段";路径";在具有@Value("${path.to.randomizer}"(的DiceRollerService类中,我将注释传递给类构造函数:

@Service
public class DiceRollerService {
private String path;
private final CustomHttpClient client;
@Autowired
public DiceRollerService(CustomHttpClient client, @Value("${path.to.randomizer}") String path) {
this.client = client;
this.path = path;
}

在这种情况下,如果我使用默认构造函数,路径字段将从应用程序属性链接到相应的值,否则,如果我用这个特定的构造函数,我可以通过它传递特定的路径值

@RunWith(MockitoJUnitRunner.class)
public class DiceRollerTest {
private static final String PATH_FOR_TEST = "Some URL path";
private static final int DICE_NUMBER_ONE = 2;
private static final int DICE_NUMBER_TWO = 2;
@Mock
private CustomHttpClient client;
private DiceRollerService diceRollerService;
@Before
public void init() {
diceRollerService = new DiceRollerService(client, PATH_FOR_TEST);
}
@Test
public void testDiceRollerServiceWithCorrectPathAndBody() {
Dices expected = new Dices(DICE_NUMBER_ONE, DICE_NUMBER_TWO);
when(client.getResponseBodyFromClient(PATH_FOR_TEST)).thenReturn(BODY_TEST);
Dices actual = diceRollerService.roll();
Assert.assertEquals(expected, actual);
verify(client).getResponseBodyFromClient(anyString());
}

初始问题

我的小项目的单元和/或集成测试需要一些帮助。我有一个类,它只有一个返回骰子模型的重要方法:

@Service
public class DiceRollerService {
@Value("${path.to.randomizer}")
private String path;
private final static int FIRST_DICE_NUMBER_POS = 0;
private final static int SECOND_DICE_NUMBER_POS = 2;
private final CustomHttpClient client;
public DiceRollerService(CustomHttpClient client) {
this.client = client;
}
public Dices roll() {
String results = client.sendGetRequest(path).body();
int firstNumber = Integer.parseInt(String.valueOf(results.charAt(FIRST_DICE_NUMBER_POS)));
int secondNumber = Integer.parseInt(String.valueOf(results.charAt(SECOND_DICE_NUMBER_POS)));
return new Dices(firstNumber, secondNumber);
}
public void setPath(String path) {
this.path = path;
}
}

正如您所看到的,在方法roll((中,我调用client.sendGetRequest(path(来获取HttpResponse的主体,并从中获取一些数据,以便构建和返回Dice模型。没有什么复杂的,只是实际路径位于我的属性文件src/main/resources/application.properties中。后来,我决定为我的项目添加一些测试来检查逻辑:

public class DiceRollerTest {
@Mock
private CustomHttpClient client;
@InjectMocks
private DiceRollerService diceRollerService;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test() {
Dices expected = new Dices(2, 2);
when(client.sendGetRequest(anyString()).body()).thenReturn("2 2 ");
Dices actual = diceRollerService.roll();
Assert.assertEquals(expected, actual);
verify(client, times(1)).sendGetRequest(anyString()).body();
}
}

不幸的是,结果a在这一行得到了一个空指针:when(client.sendGetRequest(anyString(((.body(((.thenReturn("2 2"(;我的模拟似乎没有像我预期的那样奏效。以下是我所有的依赖项:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
</dependencies>

有什么方法可以在单元测试中使用我的属性吗?我该如何正确修复?如有任何建议,我将不胜感激。提前谢谢。

此行

when(client.sendGetRequest(anyString()).body()).thenReturn("2 2 ");

被锁得太远了一步。您需要先存根client.sendGetRequest(anyString())的响应,然后才能存根.body()调用。类似的东西

when(client.sendGetRequest(anyString()).thenReturn(myMockedHttpResponse); 
when(myMockedHttpResponse.body()).thenReturn("2 2 ");

注入属性是另一个问题;考虑将属性文件放在/src/test/resources下,或者查看@TestPropertySource,或者将属性重构为@ConfigurationProperties-类并注入其中一个或。。。无尽的可能性:-(

面对这个确切的问题,我们开发了一个测试扩展,通过与@InjectMocks的集成,非常优雅地解决了这个确切问题:https://github.com/exabrial/mockito-object-injection

<dependency>
<groupId>com.github.exabrial</groupId>
<artifactId>mockito-object-injection</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>

它的工作原理是在测试中创建一个字段,名称与测试中的服务或控制器中的目标字段相同。用@InjectionSource注释标记此字段,然后在调用测试中的类之前将其设置为所需的值。非常简单:

@ExtendWith({ MockitoExtension.class, InjectExtension.class })
class MyControllerTest {
@InjectMocks
private MyController myController;
@Mock
private Logger log;
@Mock
private Authenticator auther;
@InjectionSource
private Boolean securityEnabled;

@Test
void testDoSomething_secEnabled() throws Exception {
securityEnabled = Boolean.TRUE;
myController.doSomething();
// wahoo no NPE! Test the "if then" half of the branch
}

@Test 
void testDoSomething_secDisabled() throws Exception {
securityEnabled = Boolean.FALSE;
myController.doSomething();
// wahoo no NPE! Test the "if else" half of branch
}
}

最新更新