Mary
Mary

Reputation: 1595

How to mock a sftp session

I am updating a few tests. The Before gets the sftp session. For this, the username and password have been hardcoded in the properties file. Due to security reasons, the password cannot be checked in and has to be blanked out. However the unit test fails at

private DefaultSftpSessionFactory sftpClientFactory;
private  SftpSession sftpSession;

 @Before
 public void setup() {
    sftpSession = sftpClientFactory.getSession();
    
}

This step fails with "either a password or private key is required".I would like to get a mock session, so that I dont have to provide a password.

Upvotes: 4

Views: 8387

Answers (4)

Oleksandr Arzamastsev
Oleksandr Arzamastsev

Reputation: 31

As mentioned by jannis here a better alternative is to use a fully functional test SFTP environment.

Why? Because mocking the logic requires a lot of boilerplate and does not allow one to fully test the application and its moving parts. Luckily, the test containers support is mature enough and can be used for your case.

The SFTP test container in a SpringBoot environment can be easily set up as follows:

@SpringBootTest
class SshClientDemoApplicationTests {

    @Container
    static final GenericContainer<?> SFTP = new GenericContainer<>("atmoz/sftp:latest")
            .withExposedPorts(22)
            .withCreateContainerCmdModifier(cmd -> cmd.withHostConfig(
                    new HostConfig().withPortBindings(new PortBinding(Ports.Binding.bindPort(22), new ExposedPort(22)))
            ))
            .withCommand("user:pass:::home");

    @BeforeAll
    static void start() throws IOException {
        SFTP.start();
    }

    @AfterAll
    static void stop() {
        SFTP.stop();
    }
}

The full working Spring Boot application can be downloaded here.

Upvotes: 3

jannis
jannis

Reputation: 5230

I don't know the purpose of the test in question, but as an alternative to mocking I could suggest testing against a fully functional SFTP server, just as originally intended.

The trick is to use Docker and some Docker-enablement java library like Testcontainers.

  • Choose a SFTP server Docker image (atmoz/sftp might be worth a try - judging from the number of stars and downloads) - make sure it's easily configurable (in atmoz/sftp the users can be configured using environment vars - see the documentation).
  • Use Testcontainers to set up the server in your test (environment variables can for a container can be set using GenericContainer#addEnv). Consult the quick start manuals for Junit 4 and Junit Jupiter for details.

Upvotes: 1

Marian Kl&#252;hspies
Marian Kl&#252;hspies

Reputation: 17657

In case you are using JUnit 5 Jupiter (might be easily adaptable to JUnit 4 as well) I´ve written an article on how to do it using the atmoz/sftp Docker image together with Testcontainers.

The full working example can be found here

https://overflowed.dev/blog/sftp-testing-junit-testcontainers-atmoz-sftp/

This is basically how you would define your TestContainer with SFTP

 private static final GenericContainer sftp = new GenericContainer(
            new ImageFromDockerfile()
                    .withDockerfileFromBuilder(builder ->
                            builder
                                    .from("atmoz/sftp:latest")
                                    .run("mkdir -p /home/" + USER + "/upload; chmod -R 007 /home/" + USER)
                                    .build()))
            //.withFileSystemBind(sftpHomeDirectory.getAbsolutePath(), "/home/" + USER + REMOTE_PATH, BindMode.READ_WRITE) //uncomment to mount host directory - not required / recommended
            .withExposedPorts(PORT)
            .withCommand(USER + ":" + PASSWORD + ":1001:::upload");

Upvotes: 3

xerx593
xerx593

Reputation: 13281

Regarding the unanswered/able question What's the best mock framework for Java?, a Mockito approach would be:

  1. Get the needed libraries into your (test) class path. (https://mvnrepository.com/artifact/org.mockito)

  2. Mock the SftpSession. (Via annotation private @Mock SftpSession sftpSession; ...plus the according initialization/enablement, or (manually) via sftpSession = Mockito.mock(SftpSession.class);)

    a. See, whether the SessionFactory is needed (for test) at all, if so also mock.

  3. Mock/verify/reset any interactions (within your tests) with the mocked objects. (Like Mockito.when(sftpSession.foo(x,y,z)).then... or Mockito.verify(sftpSession, Mockito.times(n)).foo(x,y,z);)

Further reading:

Upvotes: 2

Related Questions