db-mock-store

database mock store for unit testing database data


Project maintained by besok Hosted on GitHub Pages — Theme by mattgraham

Database mock store

This is a library for serialization and deserialization pojos to different formats for saving into stable removable store. This data can be used in test cases or other jobs.

Description

If we have a relational database with a bunch relations between tables we can hardly use unit tests without mocking database by h2 or other embedded solutions. This library maintains tools for doing this. Basic idea is to find base javax.persistence.* annotations(Entity,Table,Column,Id,ManyToOne,OneToOne and etc) to build entity graph from pojos. After that , based on that meta information , we can construct flat view of that pojo. It is similar as a csv file uploaded from databases. Major goal is a possibility to get this entity from csv files exported from database.\ Thus we can construct a graph depending objects by @ManyToOne and @OneToOne annotations(@OneToMany and @ManyToMany are a partial cases for previouses) For example we have a same structure:

@Table(schema=shop,name=order)
class Order{ 
    @ManyToOne Customer customer
    @ManyToOne Manager manager
    @OneToMany List<OrderBasket> baskets
}
@Table(schema=shop,name=customer)
class Customer{
    @OneToOne Address address
}
@Table(schema=shop,name=item)
class Item{
    @OneToOne Code code
    @OneToMany List<OrderBasket> baskets
}
@Table(schema=shop,name=basket)
class OrderBasket{
    @ManyToOne Order order
    @ManyToOne Item item   
}

we have a graph :

                          | -> Customer -> Address
              |-> Order ->| -> Manager
OrderBasket ->|
              |-> Item -> Code

Using this lib we can save OrderBasket to file or directory and get 7 files with depending entities corresponded to tables from database:

Files will contain columns and values separating ‘;’ and wrapped quotes.

"id";"comment";"amount";"order_time";"customer_id";"payment_id"
"1";"comment";"10";"2019-01-01T01:01";"1";""

Design

API

you have 2 options for starting work with this library:

Files have a csv similar syntax:

QueryableStore let you process followings commands:

Use Cases in tests

Simple case

Common case is we can do some programs logic without spring context by mocking components especially Repositories.

public class CommonCaseExampleTest extends AbstractJpaFileMock {
  public CommonCaseExampleTest() {
	super("your.package.with.pojos");
  }
  
  AppComponent component;

  @Before
  public void setUp() throws Exception {
	QueryableStore store = store("test_dir");
	OrderRepo orderRepo = Mockito.mock(OrderRepo.class);
	List<Order> orders = store.all(Order.class); // we can get all orders from file instead of database
	Mockito.when(orderRepo.findAll()).thenReturn(orders); 
	component = new AppComponent(orderRepo);
  }

  @Test
  public void test() {
	int result = component.doSomeLogic();
	Assert.assertEquals(42,result);
  }

  public class AppComponent {
	private OrderRepo orderRepo;

	AppComponent(OrderRepo orderRepo) {
	  this.orderRepo = orderRepo;
	}
	
	public int doSomeLogic(){
	  List<Order> orders = orderRepo.findAll();
	  
	  int res = 42; // some hard logic
	  return res;
	}
  }

}

Entities from ManyToOne

If you have structure like that:

class Customer{	
    @OneToMany List<Order> orders;
  }
class Order{
	@ManyToOne	Customer customer;
	@OneToMany List<OrderBasket> baskets;
  }
class Item{	
    @OneToMany List<OrderBasket> baskets
  }
class OrderBasket{
	@ManyToOne Order order;
	@ManyToOne Item item;
  }

and you want to drop to file all OrderBaskets from Customer you can do something like that:

List<OrderBasket> baskets = 
    customer.getOrders().stream()
		.map(Order::getBaskets)
		.flatMap(Collection::stream)
		.collect(Collectors.toList());
		
toFile(baskets,"path_for_saving");

Uncommon mapping

if you have some fields with uncommon format ,for example for date you can get StringMapException. For solving this you can do your own String mapper:

"id";"time";"name"
"1";"2019-01-01 00:00";"new_year_party"

For solving this you can do your own String mapper by implementing AbstractStringMapper:

public class UserStringMapper extends AbstractStringMapper {
  @Override
  public StringFunction<LocalDateTime> localDateTime() {
	return s -> LocalDateTime.parse(s, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
  }
}

And mix it to a invoker:

public class CommonCaseExampleTest extends AbstractJpaFileMock {
 @Before
  public void setUp() throws Exception {
    withMapper(new UserStringMapper());
  }
}
...
invoker.withMapper(new UserStringMapper());

Several datasources

if you have several databases with different packages for scan you have to have several invokers or switch one invoker all time: