Skip to content

Testosterone is architectural testing framework designed to simplify tests writing and Jersey based applications testing.

License

Notifications You must be signed in to change notification settings

stasha/testosterone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Testosterona cartoon

testosterone

is architectural testing framework designed to simplify tests writing and Jersey based applications testing.
Also, it simplifies writing "free-form" or "cowboy" tests. Beside testing it is ideal for learning/experimenting with Jersey, Helidon or CDI.

Build Status CircleCI Maven Central Coverage Status Maintainability

Testosterone is built around idea where components are tested from ground up ending with joined tests into integration.

  1. Test DAO - against in-memory or external DB
  2. Test service - using mocks
  3. Test resource - using mocks
  4. Integration test - against embedded server, in-memory or external DB
  5. Integration test - against any external environment

Take a look at example code.

Testosterone supports

Install using maven

<dependency>
    <groupId>info.stasha</groupId>
    <artifactId>testosterone</artifactId>
    <version>${testosterone.version}</version>
</dependency>

Downlod from

https://repo.maven.apache.org/maven2/info/stasha/testosterone/

Minimum dependencies for running tests

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-grizzly2-servlet</artifactId>
    <version>2.27</version>
</dependency>

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-grizzly2-http</artifactId>
    <version>2.27</version>
</dependency>

<!-- Required for Jersey version 2.26 and higher. For lower versions
this dependency should be removed because it makes conflicts -->
<dependency>
    <groupId>org.glassfish.jersey.inject</groupId>
    <artifactId>jersey-hk2</artifactId>
    <version>2.27</version>
</dependency>

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

<dependency>
    <groupId>info.stasha</groupId>
    <artifactId>testosterone</artifactId>
    <version>3.0.1</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

Minimum code required

JUnit4

import info.stasha.testosterone.jersey.junit4.Testosterone;
import info.stasha.testosterone.junit4.TestosteroneRunner;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(TestosteroneRunner.class)
public class JUnit4Test implements Testosterone {
    
    @Test
    public void test(){
    }
}

JUnit5

import info.stasha.testosterone.jersey.junit5.Testosterone;
import org.junit.jupiter.api.Test;

public class JUnit5Test implements Testosterone {
 
    @Test
    public void test(){
    }
}

TestNG

import info.stasha.testosterone.jersey.testng.Testosterone;
import org.testng.annotations.Test;

public class TestNGTest implements Testosterone {
 
    @Test
    public void test(){
    }
}

Test class configuration defaults

@Configuration(
  baseUri = "http://localhost/", 
  httpPort = 9998, 
  testConfig = JerseyTestConfig.class, 
  dbConfig = H2Config.class, 
  serverConfig = JettyServerConfig.class, 
  runServer = true, 
  runDb = false, 
  startServer = StartServer.PER_CLASS,
  stopServerAfterTestEnds = true
)
public class TestNGTest implements Testosterone {
 
    @Test
    public void test(){
    }
}

Examples

DAO test

@RunWith(TestosteroneRunner.class)
@Dependencies(
        UserDaoTest.class
)
@Configuration(startServer = StartServer.PER_TEST_METHOD, runDb = true)
public class TaskDaoTest implements Testosterone {

    private final String createTasksTable = "CREATE TABLE tasks (\n"
            + "  id BIGINT(11) NOT NULL auto_increment PRIMARY KEY,\n"
            + "  title VARCHAR(56) NOT NULL,\n"
            + "  description VARCHAR(56) NOT NULL,\n"
            + "  done BOOLEAN NOT NULL,\n"
            + "  users_user_id BIGINT(11) NOT NULL,\n"
            + "  created_at DATETIME,\n"
            + "  updated_at DATETIME,\n"
            + "  FOREIGN KEY (users_user_id) REFERENCES users(id)\n"
            + "  )";

    @Context
    Connection conn;

    @Context
    private TaskDao taskDao;

    @Override
    public void configure(AbstractBinder binder) {
        binder.bindFactory(TaskDaoFactory.class).to(TaskDao.class).in(RequestScoped.class).proxy(true).proxyForSameScope(false);
    }

    @Override
    public void configure(DbConfig config) {
        config.add("createTasksTable", createTasksTable);
    }

    @Override
    public void configureMocks(DbConfig config) {
        config.add("addMockTasks", "insert into tasks (title, description, done, users_user_id) values "
                + "('Create Task Test1', 'Testing TaskDao1', false, 1),"
                + "('Create Task Test2', 'Testing TaskDao2', true, 2),"
                + "('Create Task Test3', 'Testing TaskDao3', false, 3)");
    }

    @Before
    public void setUp() throws Exception {
        assertEquals("Task list should contain 3 tasks", 3, taskDao.getAllTasks().size());
    }

    @After
    public void tearDown() throws Exception {
        conn.prepareStatement("drop table tasks").executeUpdate();
    }

    @Test
    public void readAllTasks() throws Exception {
        taskDao.createTask(new Task("Create Task Test 4", "Testing TaskDao 2", Boolean.FALSE, new User(1L)));

        List<Task> tasks = taskDao.getAllTasks();
        assertEquals("Task list should contain 4 tasks", 4, tasks.size());
    }

    @Test
    public void readTask() throws Exception {
        Task dbtask = taskDao.getTask(new Task(3L));
        assertNotNull("Returned task from db should not be null", dbtask);
    }

    @Test
    public void updateTask() throws Exception {
        this.taskDao.updateTask(new Task(2L).setTitle("Update Task Test").setDescription("Updating testing TaskDao").setDone(Boolean.TRUE));

        Task dbtask = taskDao.getTask(new Task(2L));
        assertNotNull("Returned task sould not be null", dbtask);
        assertEquals("Title should be updated", "Update Task Test", dbtask.getTitle());
        assertEquals("Description should be updated", "Updating testing TaskDao", dbtask.getDescription());
        assertEquals("Done should be true", true, dbtask.getDone());
    }

    @Test
    public void deleteTask() throws Exception {
        this.taskDao.deleteTask(new Task(1L));

        Task dbtask = taskDao.getTask(new Task(1L));
        assertNull("Returned task should be null", dbtask);
    }

    @Test(expected = NullPointerException.class)
    public void createSqlException() throws SQLException {
        this.taskDao.createTask(new Task());
    }

}

Service test

@RunWith(TestosteroneRunner.class)
public class TaskServiceTest implements Testosterone {

    @Context
    private TaskService taskService;

    @Context
    private TaskDao taskDao;

    private final Task task = new Task("New Task", "New Task Description", false).setId(1L);

    @Override
    public void configure(AbstractBinder binder) {
        binder.bindFactory(TaskServiceFactory.class).to(TaskService.class).in(RequestScoped.class).proxy(true).proxyForSameScope(false);
    }

    @Override
    public void configureMocks(AbstractBinder binder) {
        binder.bindFactory(FactoryUtils.<TaskDao>mock(TaskDaoFactory.class)).to(TaskDao.class).in(Singleton.class);
    }

    public <T> T verify(T mock, int invocations) {
        return Mockito.verify(mock, times(invocations));
    }

    @Test(expected = IllegalArgumentException.class)
    public void createTest() throws Exception {
        taskService.createTask(new Task(1L));
    }

    @Test(expected = NullPointerException.class)
    public void createTest2() throws Exception {
        taskService.createTask(null);
    }

    @Test
    public void createTest3() throws Exception {
        taskService.createTask(task.setId(null));
        verify(taskDao, 1).createTask(task.setId(null));
    }

    @Test(expected = IllegalArgumentException.class)
    public void readTest() throws Exception {
        taskService.getTask(new Task());
    }

    @Test(expected = NullPointerException.class)
    public void readTest2() throws Exception {
        taskService.getTask(null);
    }

    @Test
    public void readTest3() throws Exception {
        taskService.getTask(task);
        Mockito.verify(taskDao, times(1)).getTask(task);
    }

    @Test(expected = IllegalArgumentException.class)
    public void updateTest() throws Exception {
        taskService.updateTask(new Task());
    }

    @Test(expected = NullPointerException.class)
    public void updateTest2() throws Exception {
        taskService.updateTask(null);
    }

    @Test
    public void updateTest3() throws Exception {
        taskService.updateTask(task);
        Mockito.verify(taskDao, times(1)).updateTask(task);
    }

    @Test(expected = IllegalArgumentException.class)
    public void deleteTest() throws Exception {
        taskService.deleteTask(new Task());
    }

    @Test(expected = NullPointerException.class)
    public void deleteTest2() throws Exception {
        taskService.deleteTask(null);
    }

    @Test
    public void deleteTest3() throws Exception {
        taskService.deleteTask(task);
        Mockito.verify(taskDao, times(1)).deleteTask(task);
    }

}

Resource test

@RunWith(TestosteroneRunner.class)
public class TaskResourceTest implements Testosterone {

    protected final Task task = new Task(3L, "Title", "Description", false);
    protected final Task createTask = new Task("Title", "Description", false);
    public Entity taskEntity = Entity.json(task);
    public Entity createTaskEntity = Entity.json(createTask);

    private final TaskResource taskResource = Mockito.spy(new TaskResource());

    @Context
    protected TaskService taskService;

    @Override
    public void configure(ResourceConfig config) {
        config.register(taskResource);
    }

    @Override
    public void configureMocks(AbstractBinder binder) {
        binder.bindFactory(FactoryUtils.<TaskService>mock(TaskServiceFactory.class)).to(TaskService.class).in(Singleton.class);
    }

    public <T> T verify(T mock, int invocations) {
        return Mockito.verify(mock, times(invocations));
    }

    @Test
    @Request(url = "task", method = GET, expectedStatus = {200, 204})
    public void getAllTasks(Response resp) throws Exception {
        verify(taskResource, 1).getAllTasks();
        verify(taskService, 1).getAllTasks();
    }

    @Test
    @Request(url = "task", method = POST, entity = "taskEntity", expectedStatus = {200, 204})
    public void createTask(Response resp) throws Exception {
        verify(taskResource, 1).createTask(Matchers.refEq(task));
        verify(taskService, 1).createTask(Matchers.refEq(task));
    }

    @Test
    @Request(url = "task/1", method = GET, expectedStatus = {200, 204})
    public void getTask(Response resp) throws Exception {
        verify(taskResource, 1).getTask(any(Task.class));
        verify(taskService, 1).getTask(any(Task.class));
    }

    @Test
    @Request(url = "task/1", method = PUT, entity = "taskEntity", expectedStatus = {200, 204})
    public void updateTask(Response resp) throws Exception {
        verify(taskResource, 1).updateTask(Matchers.refEq(task));
        verify(taskService, 1).updateTask(Matchers.refEq(task));
    }

    @Test
    @Request(url = "task/1", method = DELETE, expectedStatus = {200, 204})
    public void deleteTask(Response resp) throws Exception {
        verify(taskResource, 1).deleteTask(any(Task.class));
        verify(taskService, 1).deleteTask(any(Task.class));
    }

}

Local integration test

@Integration({
    TaskResourceTest.class,
    TaskServiceTest.class,
    TaskDaoTest.class,
    UserResourceTest.class,
    UserServiceTest.class,
    UserDaoTest.class
})
@RunWith(TestosteroneRunner.class)
@Configuration(runDb = true)
public class TaskEndpointIntegrationTest implements Testosterone {

    protected TaskResource taskResource;
    protected final Task task = new Task(3L, "Title", "Description", false, new User(2L));
    protected final Task createTask = new Task("Title", "Description", false, new User(1L));
    public Entity taskEntity = Entity.json(task);
    public Entity createTaskEntity = Entity.json(createTask);

    @Before
    public void setUp() throws SQLException {
        target("user").request().post(Entity.json(new User("Jon", "Doe", 23)));
    }

    @Test
    @Request(url = "task", method = GET, expectedStatus = {200, 204})
    public void getAllTasks(Response resp) throws Exception {
    }

    @Test
    @Request(url = "task", method = POST, entity = "taskEntity", expectedStatus = {500})
    public void failCreateTask(Response resp) throws Exception {
    }

    @Test
    @Request(url = "task", method = POST, entity = "createTaskEntity", expectedStatus = {200, 204})
    public void createTask(Response resp) throws Exception {
    }

    @Test
    @Request(url = "task/1", method = GET, expectedStatus = {200, 204})
    public void getTask(Response resp) throws Exception {
    }

    @Test
    @Request(url = "task/1", method = PUT, entity = "taskEntity", expectedStatus = {200, 204})
    public void updateTask(Response resp) throws Exception {
    }

    @Test
    @Request(url = "task/1", method = DELETE, expectedStatus = {200, 204})
    public void deleteTask(Response resp) throws Exception {
    }

}

External/remote integration test

// Just extend local integration test and change configuration
@Configuration(baseUri = "http://myapp.com/", httpPort = 80, runServer = false)
public class TaskEndpointExternalIntegrationTest extends TaskEndpointIntegrationTest {

}

About

Testosterone is architectural testing framework designed to simplify tests writing and Jersey based applications testing.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages