Integration with Spring Framework
Sniffy comes with a Spring Framework via SniffySpringTestListener
spring @TestExecutionListener.
Just add @TestExecutionListeners(SniffySpringTestListener.class)
to your Spring test class and place appropriate expectations on your test methods like shown below.
package io.sniffy.test.spring.usage;
import io.sniffy.socket.DisableSockets;
import io.sniffy.sql.SqlExpectation;
import io.sniffy.test.Count;
import io.sniffy.test.spring.SniffySpringTestListener;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.sql.Connection;
import java.sql.SQLException;
import static java.sql.DriverManager.getConnection;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringUsageTest.class)
@TestExecutionListeners(value = SniffySpringTestListener.class, mergeMode = MERGE_WITH_DEFAULTS) // (1)
public class SpringUsageTest {
@Test
@SqlExpectation(count = @Count(max = 1))
public void testJUnitIntegration() throws SQLException {
final Connection connection = getConnection(
"sniffy:jdbc:h2:mem:", "sa", "sa");
connection.createStatement().execute("SELECT 1 FROM DUAL");
}
@Test
@DisableSockets // (5)
public void testDisableSockets() throws IOException {
try {
new Socket("google.com", 443); // (6)
fail("Sniffy should have thrown ConnectException");
} catch (ConnectException e) {
assertNotNull(e);
}
}
}
-
- Integrate Sniffy to your test using
@TestExecutionListeners(SniffySpringTestListener.class)
. -
- Now just add
@SqlExpectation
annotation to define number of queries allowed for given method. -
- Just add
sniffy:
in front of your JDBC connection URL in order to enable sniffer. -
- Do not make any changes in your code - just add the
@TestExecutionListeners(SniffySpringTestListener.class)
and put annotations on your test method. -
- Add
@DisableSockets
annotation on your test method or test class and any attempt to open a network connection will fail -
- All socket operations executed within test method annotated with
@DisableSockets
will throw ajava.net.ConnectException
@SharedConnection
Sniffy provides convenient annotations for shared connection data source in Spring unit tests. Consider example below:
package io.sniffy.test.spring.usage;
import io.sniffy.test.spring.DataSourceTestConfiguration;
import io.sniffy.test.spring.EnableSharedConnection;
import io.sniffy.test.spring.SharedConnection;
import io.sniffy.test.spring.SniffySpringTestListener;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import ru.yandex.qatools.allure.annotations.Features;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.junit.Assert.assertEquals;
import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceTestConfiguration.class, SpringSharedConnectionUsageTest.class})
@TestExecutionListeners(value = SniffySpringTestListener.class, mergeMode = MERGE_WITH_DEFAULTS) // (1)
@EnableSharedConnection // (2)
@Transactional
@Rollback
public class SpringSharedConnectionUsageTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
@SharedConnection // (3)
@Features("issue/344")
public void testSharedConnectionTwoRows() throws SQLException, ExecutionException, InterruptedException {
jdbcTemplate.batchUpdate(
"INSERT INTO PUBLIC.PROJECT (ID, NAME) VALUES (SEQ_PROJECT.NEXTVAL, ?)",
Arrays.asList(new Object[]{"foo"}, new Object[]{"bar"})
); // (4)
assertEquals(2, newSingleThreadExecutor().submit(
() -> jdbcTemplate.queryForObject("SELECT COUNT(*) FROM PUBLIC.PROJECT", Integer.class) // (5)
).get().intValue());
}
}
-
- Integrate Sniffy to your test using
@TestExecutionListeners(SniffySpringTestListener.class)
. -
-
@EnableSharedConnection
will automatically wrap all your data sources withSharedConnectionDataSource
-
-
@SharedConnection
annotation will make the current connection (started because of@Transactional
annotation before each test) as master -
- Insert two rows into table using master connection
-
- Another slave connection obtained in another thread will still see the results of these inserts although the isolation level is
READ_COMITTED
and master transaction has not been committed
Troubleshooting
-
Spring test context is no longer loaded when I add
@TestExecutionListeners(SniffySpringTestListener.class)
The reason is that by default Spring does not merge the test execution listeners. Using this configuration you’re removing the predefined listeners which initialize the context. Change the mergeMode parameter to MERGE_WITH_DEFAULTS as shown below:
@TestExecutionListeners(value = SniffySpringTestListener.class, mergeMode = MERGE_WITH_DEFAULTS)