2017-09-27 1 views
1

Ich schreibe einen Komponententest, in dem ich versuche, einen Dienst teilweise zu verspotten. Was ich meine ist, ich möchte eine der Methoden des Dienstes, um ein anderes verspottetes Objekt und eine andere Methode zurückgeben, um sich normal zu verhalten. Dies ist die Methode Ich teste:Zurückgegebenes Objekt von teilweise verspottetem Objekt funktioniert nicht

public async Task<List<string>> GetDeletedRecordIds<T>(DateTime startDate) 
     where T : ISalesForceObject 
    { 
     List<string> result; 
     try 
     { 
      var client = await this.GetForceClient(); 
      var init = await client.GetDeleted<DeletedRecordRootObject>(typeof(T).Name, startDate, DateTime.Now); 
      result = init?.DeletedRecords.Select(d => d.Id).ToList(); 
     } 
     catch (Exception e) 
     { 
      this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, "GetDeletedRecordIds"); 
      throw; 
     } 

     return result; 
    } 

Dies ist die Methode, die ich brauche ein verspottete Objekt zurückgeben:

public async Task<IForceClient> GetForceClient() 
    { 
     ForceClient forceClient = null; 
     try 
     { 
      var auth = new AuthenticationClient(); 

      var consumerKey = this._settingService.GetSetting("SalesForceConsumerKey"); 
      var consumerSecret = this._settingService.GetSetting("SalesForceConsumerSecret"); 
      var password = this._settingService.GetSetting("SalesForcePassword"); 
      var securityToken = this._settingService.GetSetting("SalesForceSecurityToken"); 
      var username = this._settingService.GetSetting("SalesForceUsername"); 
      var tokenUrl = $"{this._settingService.GetSetting("SalesForceUrl")}/services/oauth2/token"; 

      await auth.UsernamePasswordAsync(
       consumerKey, 
       consumerSecret, 
       username, 
       password + securityToken, 
       tokenUrl); 

      forceClient = new ForceClient(auth.InstanceUrl, auth.AccessToken, auth.ApiVersion); 
     } 
     catch (Exception e) 
     { 
      this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"GetForceClient"); 
      throw; 
     } 

     return forceClient; 
    } 

Und das ist, was ich derzeit in meinem Unit-Test habe:

 var mockForceClient = new Mock<IForceClient>(); 
     mockForceClient 
      .Setup(
       i => i.GetDeleted<DeletedRecordRootObject>(
        It.IsAny<string>(), 
        It.IsAny<DateTime>(), 
        It.IsAny<DateTime>())).ReturnsAsync(deletedRecordRootObject); 

     var mockService = new Mock<IForceDotComService>(); 
     mockService.Setup(m => m.GetDeletedRecordIds<sf.Account>(It.IsAny<DateTime>())) 
      .Returns(async (DateTime d) => await this._service.GetDeletedRecordIds<sf.Account>(d)); 
     mockService.Setup(m => m.GetForceClient()) 
      .ReturnsAsync(mockForceClient.Object); 

Derzeit wird der Test in GetDeletedRecordIds ausgeführt, bis es den Aufruf der GetForceClient Methode trifft. Dann, anstatt das verspottete ForceClient-Objekt zurückzugeben, versucht es tatsächlich, die Methode auszuführen, die natürlich fehlschlägt.

Vielen Dank im Voraus für jede Hilfe.

LÖSUNG: So habe ich mein Problem gelöst.

Zuerst habe ich einen Dienst die ForceClient zurückzukehren wie folgt:

public class ForceClientService : IForceClientService 
{ 
    private readonly ILogger _logger; 

    private readonly ISettingService _settingService; 

    public ForceClientService(
     ILogger<ForceClientService> logger, 
     ISettingService settingService) 
    { 
     this._logger = logger; 
     this._settingService = settingService; 
    } 

    public async Task<IForceClient> GetForceClient() 
    { 
     ForceClient forceClient = null; 
     try 
     { 
      var auth = new AuthenticationClient(); 

      var consumerKey = this._settingService.GetSetting("SalesForceConsumerKey"); 
      var consumerSecret = this._settingService.GetSetting("SalesForceConsumerSecret"); 
      var password = this._settingService.GetSetting("SalesForcePassword"); 
      var securityToken = this._settingService.GetSetting("SalesForceSecurityToken"); 
      var username = this._settingService.GetSetting("SalesForceUsername"); 
      var tokenUrl = $"{this._settingService.GetSetting("SalesForceUrl")}/services/oauth2/token"; 

      await auth.UsernamePasswordAsync(
       consumerKey, 
       consumerSecret, 
       username, 
       password + securityToken, 
       tokenUrl); 

      forceClient = new ForceClient(auth.InstanceUrl, auth.AccessToken, auth.ApiVersion); 
     } 
     catch (Exception e) 
     { 
      this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"GetForceClient"); 
      throw; 
     } 

     return forceClient; 
    } 
} 

Dann änderte ich die Methode, die ich testen bin:

public async Task DeleteRecord<TSf>(TSf record) 
     where TSf : ISalesForceObject 
    { 
     try 
     { 
      var client = await this._forceClientService.GetForceClient(); 
      var response = await client.DeleteAsync(typeof(TSf).Name, record.Id); 
      if (!response) 
      { 
       throw new Exception($"Error deleting record with ID {record.Id}"); 
      } 
     } 
     catch (Exception e) 
     { 
      this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"ForceDotComService.DeleteRecord"); 
      throw; 
     } 
    } 

Dann wieder aufgebaut ich meine mock die Abhängigkeiten zu verspotten vs Die Methoden:

var mockForceClient = new Mock<IForceClient>(); 
     mockForceClient 
      .Setup(
       i => i.GetDeleted<DeletedRecordRootObject>(
        It.IsAny<string>(), 
        It.IsAny<DateTime>(), 
        It.IsAny<DateTime>())).ReturnsAsync(deletedRecordRootObject); 

     var mockLogger = new Mock<ILogger<ForceDotComService>>(); 
     var mockForceClientService = new Mock<IForceClientService>(); 
     mockForceClientService.Setup(m => m.GetForceClient()).ReturnsAsync(mockForceClient.Object); 

     this._service = new ForceDotComService(mockLogger.Object, mockForceClientService.Object); 

Es funktioniert jetzt wie erwartet. Vielen Dank für die Hilfe!

+1

Nun, natürlich haben Sie 'neue ForceClient()' in dort, so dass es nicht Ihre Spott verwendet. Injizieren Sie diese Abhängigkeit. Abgesehen davon, gibt es nicht genug relevanten Code in Ihrer Frage, um eine richtige Antwort zu geben, also muss dies tun. – CodeCaster

+0

Sie sollten kein Modell der Klasse erstellen, die getestet wird. Außerdem sollten Sie die Methode der Klasse, die getestet wird, nicht einrichten. Mocks sollten von den Abhängigkeiten erstellt werden, von denen diese Klasse abhängig ist.Sieht so aus, als ob deine Klasse von _settingService abhängig ist. Ich denke, Sie müssen Mock von "_settingService" und Setup-Methode "GetSetting" für verschiedene Parameter erstellen. –

Antwort

2

Extract this.GetForceClient() hinaus in seinen eigenen Dienst durch einen würden Sie

public IForceClientProvider { 
    Task<IForceClient> GetForceClient(); 
} 

Abstraktion gesichert dann Ihre aktuelle Klasse unter Test Refactoring explizit über Konstruktor Injektion an dieser Schnittstelle abhängen.

public class ForceDotComService : IForceDotComService { 
    private readonly IForceClientProvider provider; 

    public ForceDotComService(IForceClientProvider provider) { 
     this.provider = provider; 
    } 

    public async Task<List<string>> GetDeletedRecordIds<T>(DateTime startDate) 
     where T : ISalesForceObject { 
     List<string> result; 
     try { 
      var client = await provider.GetForceClient(); 
      var init = await client.GetDeleted<DeletedRecordRootObject>(typeof(T).Name, startDate, DateTime.Now); 
      result = init?.DeletedRecords.Select(d => d.Id).ToList(); 
     } catch (Exception e) { 
      this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, "GetDeletedRecordIds"); 
      throw; 
     } 

     return result; 
    } 
} 

Dies würde Ihnen dann ermöglichen, das gewünschte Verhalten beim Testen zu verspotten. Im Implementierungscode hätten Sie den gleichen Code wie oben in der GetForceClient() Methode dargestellt.

2

Sie spotten Abhängigkeiten, nicht Methoden auf der Klasse im Test.

Sie müssen die Abhängigkeit IForceClient injizieren, indem Sie sie zum Beispiel zu einem Konstruktorparameter machen. Denn jetzt wird Ihr GetForceClient() einfach für die getestete Klasse aufgerufen, die in dieser Klasse und nicht auf Ihrem Mock läuft, und gibt einfach die dort angegebene new ForceClient() zurück.

Verwandte Themen