Poiché le risorse sono esterne, nel tuo caso Config
e Service
, rende un po 'difficile da testare. Fondamentalmente hai due opzioni:
- Vai con il flusso e imposta le risorse esterne sotto il tuo controllo, se possibile
- Rifatta il codice in modo che sia più facile prendere in giro le risorse
Dato che il primo che chiaramente non puoi fare, passeremo attraverso il secondo. Refactoring del codice con ogni passo che risolve ogni dipendenza in modo che siano sostituibili per il test:
Correzione della dipendenza da Config
Suppongo che Config
sia una risorsa statica, il che significa che devi essere in grado di sostituirlo in qualche modo. La cosa più semplice è aggiungere una nuova risorsa sostituibile:
public interface IConfigResource {
String getProperty(String propertyName);
}
Insieme a una versione statica predefinita di dandy:
public class DefaultConfigResource : IConfigResource {
public String getProperty(String propertyName) {
return Config.getProperty(propertyName);
}
}
Nella tua classe devi aggiungere / modificare quanto segue in modo che la dipendenza possa essere iniettata per il test:
// The default is set at construction
private IConfigResource configResource = new DefaultConfigResource();
// Method used by the test to inject the config resource
public void setConfigResource(IConfigResource configResource) {
this.configResource = configResource;
}
Ora possiamo utilizzare Mockito o manualmente manualmente una versione falsa di IConfigResource
per verificare che il tuo metodo sia chiamando correttamente la risorsa config, nel test in basso usiamo Mockito:
ClassUnderTest cut;
IConfigResource mockedResource;
@Before
public void setup() {
cut = ... ; // setup for class under test
// We mock with mockito and inject the resource
mockedResource = mock(IConfigResource.class);
cut.setConfigResource(mockedConfigResource);
}
@Test
public void callingMethod_ShouldGetValuesFromResource() {
// Arrange
// might change the return values to reflect the correct ones
when(mockedResource.getProperty("serviceurl"))
.thenReturn("correct service url");
when(mockedResource.getProperty("serviceusername"))
.thenReturn("correct service username");
when(mockedResource.getProperty("servicepassword"))
.thenReturn("correct service password");
// Act
cut.methodToTest("username");
// Assert, with mockito
verify(mockedResource).getProperty("serviceurl");
verify(mockedResource).getProperty("serviceusername");
verify(mockedResource).getProperty("servicepassword");
}
Lì, la parte% del parametro% co_de del metodo dovrebbe ora essere testata
Correzione della dipendenza da Config
(e rimozione della dipendenza Config come bonus)
Quindi abbiamo bisogno di qualcosa che deve gestire questa parte del codice:
//Connect to an external service.
Service service = new Service(url, username, password);
//invoke the method on the service.
String returnValue = service.registerUser(user);
Per prima cosa il servizio deve avere un'interfaccia sostituibile per consentirci di verificare che il tuo metodo stia chiamando Service
. Questo dovrebbe essere facile:
public interface IService {
String registerUser(String userName);
}
Il tuo attuale servizio può implementare questa interfaccia con facilità, basta aggiungerla alla firma della classe (poiché dovrebbe avere già il metodo):
public class Service implements IService {
...
Abbiamo anche bisogno di qualcosa (un metodo di fabbrica) che possa connettersi al servizio e restituire qualcosa che assomiglia a service.registerUser(String)
. Possiamo anche aggiungere la risorsa di configurazione che abbiamo creato sopra per renderla un po 'più semplice:
public interface IServiceConnector {
void setConfigResource(IConfigResource configResource);
// no need to have parameters, the config is injected above
IService getService();
}
L'implementazione di default è semplice, possiamo usare IService
che abbiamo usato prima:
public class DefaultServiceConnector {
IConfigResource configResource = new DefaultConfigResource();
public void setConfigResource(IConfigResource configResource) {
this.configResource = configResource;
}
public IService getService() {
String url = Config.getProperty("serviceurl");
String username = Config.getProperty("serviceusername");
String password = Config.getProperty("servicepassword");
return new Service(url, username, password);
}
}
Ora puoi spostare il test scritto sopra per essere utilizzato come test per questa classe di connettori in cui il metodo in prova è ConfigResource
. Inoltre il tuo metodo originale sotto test dovrebbe essere più semplice ora:
IServiceConnector serviceConnector = new DefaultServiceConnector();
public void setServiceConnector(IServiceConnector serviceConnector) {
this.serviceConnector = serviceConnector;
}
@Override
public void methodToTest(String user) {
// ... Removed some code ...
//Connect to an external service.
Service service = serviceConnector.getService();
//invoke the method on the service.
String returnValue = service.registerUser(user);
if (returnValue.equals("failure")){
throw new Exception("User could not be registered");
}
}
Il test dovrebbe essere simile a questo ora:
ClassUnderTest cut;
IServiceConnector mockedConnector;
IService mockedService;
@Before
public void setup() {
cut = ... ; // setup for class under test
// We mock with mockito and inject the resource
mockedConnector = mock(IServiceConnector.class);
cut.setServiceConnector(mockedConnector);
// We mock the service to be used
mockedService = mock(IService.class);
}
@Test
public void registerUser_ShouldRegisterToService() {
// Arrange
// might change the return values to reflect the correct ones
when(mockedConnector.getService())
.thenReturn(mockedService);
String newUsername = "newusername";
// Act
cut.methodToTest(newUsername);
// Assert, with mockito
// we only need to assert that the service registers the
// new user
verify(mockedService).registerUser(username);
}
Puoi anche testare il lancio delle eccezioni:
// test the exception
@Test(expected = Exception.class)
public void registerUser_ThrowExceptionOnError() {
// Arrange
when(mockedConnector.getService())
.thenReturn(mockedService);
// return failure on any string
when(mockedService.registerUser(anyString))
.thenReturn("failure");
String newUsername = "newusername";
// Act
cut.methodToTest(newUsername);
// Assert
// Should throw exception and be checked with the
// expected parameter in test
}
Spero che questo ti aiuti.