2013-07-02 5 views
0

У меня было чертовски время, пытаясь получить фальшивую фреймворк и запустить, что может проверить мою SFTP-службу. Я был знаком с EasyMock, PowerMock и JMockit, но закончил работу с GMock. test ('org.gmock:gmock:0.8.2') { excludes 'junit' }Ошибка Grails GMock при издевательстве OutputStream

Теперь, когда у меня есть успешный тест на счастливый путь, я пишу свою логику повтора, а затем мои сценарии сбоев. У меня теперь есть две проблемы. Я не могу найти решения на них, поскольку почти все для Grails и GMock мало документированы.

Метод испытания: Я использую пример this blog's SFTP with JCraft's JSch и немного расширил его, чтобы соответствовать моим потребностям. Я беру учетные данные для подключения и имени файла. Я создаю FileOutputStream, а затем подключаюсь к SFTP-серверу. Если я получу исключение, я повторю его nth раз (упрощенный здесь для целей SO).

/** 
* Transfers the file from the remote input server to the local output server. 
* 
* @param fileName 
*  - the file name 
* @param inputFtpCredential 
*  - the input server 
* @param outputFtpCredential 
*  - the output server 
* @param mode 
*  - the mode for the transfer (defaults to {@link ChannelSftp#OVERWRITE} 
* @throws SftpException if any IO exception occurs. Anything other than 
*  {@link ChannelSftp#SSH_FX_NO_SUCH_FILE SSH_FX_NO_SUCH_FILE} or {@link 
*  ChannelSftp#SSH_FX_PERMISSION_DENIED SSH_FX_PERMISSION_DENIED} may cause a retry 
*/ 
public void transferRemoteToLocal(String fileName, FtpCredential inputFtpCredential, FtpCredential outputFtpCredential, Integer mode = ChannelSftp.OVERWRITE) { 
    for (retryCounter in 0 .. maxRetries) { 
     FileOutputStream output 
     try { 
      File file = new File(outputFtpCredential.remoteBaseDir, fileName); 
      // set stream to append if the mode is RESUME 
      output = new FileOutputStream(file, (mode == ChannelSftp.RESUME)); 

      /* 
      * getting the file length of the existing file. This is only used 
      * if the mode is RESUME 
      */ 
      long fileLength = 0 
      if (file.exists()) 
       fileLength = file.length() 
      load (output, fileName, inputFtpCredential, mode, fileLength) 
      // success 
      return 
     } catch (exception) { 
      // if an exception is thrown then retry a maximum number of times 
      if (retryCounter < maxRetries) { 
       // let the thread sleep so as to give time for possible self-resets 
       log.info "Retry number ${retryCounter+1} of file $fileName transfer after $sleepDuration ms" 
       Thread.sleep(sleepDuration) 
       mode = ChannelSftp.RESUME 
      } else { 
       int exceptionID = (exception instanceof SftpException)?(exception as SftpException).id:0 
       throw new SftpException(exceptionID, "Max number of file transfer retries ($maxRetries) exceeded on file $fileName", exception) 
      } 
     } finally { 
      if (output != null) 
       output.close() 
     } 
    } 
} 

def load(OutputStream outputStream, String fileName, FtpCredential ftpCredential, Integer mode, Long fileIndex = 0) 
throws SocketException, IOException, SftpException, Exception { 
    connect(ftpCredential) { ChannelSftp sftp -> 
     sftp.get(fileName, outputStream, mode, fileIndex) 
    } 
} 

Так что это работает в сочетании с методами из блога. Я написал сценарий моего счастливого пути и начал работать с GMock.

public void testSavingRemoteToLocal_Success() throws JSchException { 
    // Holders for testing 
    String fileToTransfer = 'test_large_file.txt' 
    FtpCredential localCredential = new FtpCredential() 
    // populate credential 
    FtpCredential remoteCredential = new FtpCredential() 
    // populate credential 

    // Mocks 
    File mockFile = mock(File, constructor(localCredential.remoteBaseDir, fileToTransfer)) 
    mockFile.exists().returns(false) 

    FileOutputStream mockFOS = mock(FileOutputStream, constructor(mockFile, false)) 

    // connection 
    JSch mockJSch = mock(JSch, constructor()) 
    Session mockSession = mock(Session) 
    ChannelSftp mockChannel = mock(ChannelSftp) 

    mockJSch.getSession(remoteCredential.username, remoteCredential.server, remoteCredential.port).returns(mockSession) 
    mockSession.setConfig ("StrictHostKeyChecking", "no") 
    mockSession.password.set(remoteCredential.password) 
    mockSession.connect().once() 
    mockSession.openChannel("sftp").returns(mockChannel) 
    mockChannel.connect() 
    mockChannel.cd(remoteCredential.remoteBaseDir).once() 

    // transfer 
    mockChannel.get (fileToTransfer, mockFOS, ChannelSftp.OVERWRITE, 0) 

    // finally method mocks 
    mockChannel.exit() 
    mockSession.disconnect() 
    mockFOS.close() 

    // Test execution 
    play { 
     service.transferRemoteToLocal(fileToTransfer, remoteCredential, localCredential) 
    } 
} 

Ошибка 1: я сделал простой копировать/вставить и не стал ничего менять, кроме имени метода испытаний, и я получаю следующее сообщение об ошибке:

java.lang.StackOverflowError 
at java.lang.ref.SoftReference.get(SoftReference.java:93) 
at org.codehaus.groovy.util.ManagedReference.get(ManagedReference.java:41) 
at org.codehaus.groovy.util.ManagedConcurrentMap$Entry.isEqual(ManagedConcurrentMap.java:62) 
at org.codehaus.groovy.util.AbstractConcurrentMap$Segment.getOrPut(AbstractConcurrentMap.java:91) 
at org.codehaus.groovy.util.AbstractConcurrentMap.getOrPut(AbstractConcurrentMap.java:35) 
at org.codehaus.groovy.reflection.ClassInfo.getClassInfo(ClassInfo.java:103) 
at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:227) 
at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751) 
at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallStaticSite(CallSiteArray.java:59) 
at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallSite(CallSiteArray.java:146) 
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) 
at org.codehaus.groovy.runtime.callsite.StaticMetaClassSite.call(StaticMetaClassSite.java:55) 
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) 
at org.codehaus.groovy.runtime.callsite.StaticMetaClassSite.call(StaticMetaClassSite.java:55) 
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) 
at org.codehaus.groovy.runtime.callsite.StaticMetaClassSite.call(StaticMetaClassSite.java:55) 
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42) 

и это продолжается какое-то время.

Ошибка 2: Затем я решил прокомментировать счастливый путь и выполнить сценарий повтора. Поэтому я стараюсь использовать везде (2), и ему не понравилось .times (2) в конструкторе. Если я этого не делаю, он жалуется, потому что конструктор вызывается дважды, поскольку повтор завершает все, а затем повторно запускает его при повторной попытке.

Я тогда попытался создать два макета всего до отказа, и это бросает какой-то NPE во время создания второго FileOutputStream mock. Кажется, он делает сравнение в файле.

public void testSavingRemoteToLocal_RetryOnce() throws JSchException { 
    // Holders for testing 
    String fileToTransfer = 'test_large_file_desktop.txt' 
    FtpCredential localCredential = new FtpCredential() 
    // populate credential 
    FtpCredential remoteCredential = new FtpCredential() 
    // populate credential 

    // Mocks 
    // First loop that fails 
    File mockFile2 = mock(File, constructor(inputCredential.remoteBaseDir, fileToTransfer)) 
    mockFile2.exists().returns(false) 

    FileOutputStream mockFIO2 = mock(FileOutputStream, constructor(mockFile2, false)) 

    // connection 
    JSch mockJSch2 = mock(JSch, constructor()) 
    Session mockSession2 = mock(Session) 

    mockJSch2.getSession(outputCredential.username, outputCredential.server, outputCredential.port).returns(mockSession2) 
    mockSession2.setConfig ("StrictHostKeyChecking", "no") 
    mockSession2.password.set(outputCredential.password) 
    mockSession2.connect().raises(new SftpException(0, "throw an exception to retry")) 
    mockSession2.disconnect() 
    mockFIO2.close() 

    // second loop that passes 
    File mockFile = mock(File, constructor(inputCredential.remoteBaseDir, fileToTransfer)) 
    mockFile.exists().returns(false) 

    FileOutputStream mockFIO = mock(FileOutputStream, constructor(mockFile, true)) // <-- Fails here with a NPE in mockFile.compareTo 

    // connection 
    JSch mockJSch = mock(JSch, constructor()) 
    Session mockSession = mock(Session) 
    ChannelSftp mockChannel = mock(ChannelSftp) 

    mockJSch.getSession(outputCredential.username, outputCredential.server, outputCredential.port).returns(mockSession) 
    mockSession.setConfig ("StrictHostKeyChecking", "no") 
    mockSession.password.set(outputCredential.password) 
    mockSession.connect() 
    mockSession.openChannel("sftp").returns(mockChannel) 
    mockChannel.connect() 
    mockChannel.cd(outputCredential.remoteBaseDir) 

    // transfer 
    mockChannel.get (fileToTransfer, mockFIO, FtpMonitor.getInstance(assetId), ChannelSftp.RESUME, 0) 

    // finally method mocks 
    mockChannel.exit() 
    mockSession.disconnect() 
    mockFIO.close() 

    // Test execution 
    play { 
     service.sleepDuration = 200 
     service.sftpCopyFrom(outputCredential, inputCredential, fileToTransfer, assetId) 
    } 

    // Assert the results 
} 
+0

Я обнаружил, что могу по крайней мере создать сценарий сбоя, если у меня есть 'new File (...)' вне цикла. Но это повлияет на мою логику повтора, если файл частично передан? Мне нужно получить текущую длину файла, и я не хочу, чтобы файл хранился в памяти при смене потока. –

ответ

1

Вы пробовали Gmock 0.8.3? Я помню, что я исправил некоторые ошибки, связанные с этим.

+0

Теперь я могу запускать свои тесты с хорошим ходом и повторить попытку. Я больше не получаю ошибку StackOverflow; однако я все еще получаю NPE, когда у меня есть файл, объявленный/созданный внутри цикла. Это происходит, когда FileOutputStream сконструирован с файловым объектом. Я не хочу объявлять файл за пределами логики повтора, потому что поток засыпает, и я не хочу сохранять файл во время повторной попытки. Я даже не знаю, почему он не использует насмешливый конструктор FOS. –

+0

Хотя я не мог заставить все работать, это меня приблизило. В конце концов они сломали Юнит. Я не согласен с этим, но иногда клиент не признает плохую практику, когда они делают это. –

 Смежные вопросы

  • Нет связанных вопросов^_^