I try to make a unit test with real database (mean not in-memory database).
this is my config file:
#TestConfiguration
#EnableJpaAuditing
public class TestConfig {
#Bean
#Primary
public DataSource getDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/faptv");
hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
hikariDataSource.setUsername("mansion");
hikariDataSource.setPassword("mansion");
return hikariDataSource;
}
}
This is my sample test:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#ContextConfiguration(classes = {TestConfig.class})
#DirtiesContext
#Transactional
public class BaseEntityTest {
#Autowired
private TBL120Dao dao;
#Test
#Sql("classpath:/sql/abc.sql")
public void test() {
TBL120Dto dto = new TBL120Dto();
dto.setUserId("xxx");
dto.setUsername("ccsc");
dto.setPasswordHash("passss");
dao.save(dto);
assertThat(dao.findByUserId("kkk").isPresent()).isTrue();
}
}
This is my /sql/abc.sql
insert into tbl120(user_id, password_hash, username) value ("kkk", "ssss", "Ssss");
When the test is finished:
I see in my real database: it's has new row from sql.
But it don't have the row with userId 'xxx' from the test method. And, when I debug inside test method, It only exist once record of userId 'xxx' when I try to execute dao.findAll() (note that in real database not exist this, real database only exits data from sql), it can not access real database.
how can I save and select data from real database?
Spring transactional tests rollback automatically by default. See the documentation for how to customize this.
Related
I have a controller with two parameters and need to test them via unit tests. Want to test 4 parameters, ViewBug, etc. But how I can make fake DB context and logger? I'm stuck at this moment:
[Fact]
public void IndexReturnsAViewResultWithAListOfUsers()
{
// Arrange
var mock = new Mock<AircraftsController>();
var controller = new AircraftsController(/*params*/);
// Act
// Assert
}
This is my controller:
public class AircraftsController : Controller
{
#region DbContext, Logger
public AppDbContext Context { get; }
private readonly ILogger<AircraftsController> _logger;
public AircraftsController(AppDbContext context, ILogger<AircraftsController> logger)
{
Context = context;
_logger = logger;
_logger.LogDebug(1, "NLog injected into Controller");
}
#endregion
[HttpGet]
public IActionResult Compare(int vehicle1, int vehicle2, int vehicle3, int vehicle4)
{
var planesFromDb = Context.Planes.OrderBy(x => x.BR).ToList();
planesFromDb.Insert(0, new Plane { Image = "~/images/EmptyPlane.png", Nation = "EmptyFlag", Name = "Select aircraft", VehicleId=0 });
var selectedPlanes = new List<Plane>();
ViewBag.AllPlanesSelected = planesFromDb;
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle1));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle2));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle3));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle4));
_logger.LogInformation("Log Message");
return View(selectedPlanes);
}
}
As Stephen has suggested the in-memory provider is a good option for mocking an EFCore db context. It works for most things.
I have a requirement to use Moq for 3rd party dependencies so I'd create mocks for both. For the db context I'd use EntityFrameworkCore.Testing (disclaimer, I am the author):
var mockedDbContext = Create.MockedDbContextFor<AppDbContext>();
Then for the logger I'd create a mock using Mock.Of
var mockedLogger = Mock.Of<ILogger<AircraftsController>>();
Easy one liners that you can then use to create your controller in your unit test. Overall I am an advocate of using the EFCore in-memory provider if it suits the unit test. Using mocks does have other advantages such as allowing you to verify invocations.
In .NET Core, you can take advantage of in-memory databases for unit tests. There are two options, EF In-Memory database, and SQLite In-Memory database. I prefer SQLite In-Memory because it gives you all the advantages of handling relational data, unlike EF In-Memory.
Testing with the EF In-Memory Database
SQLite In-Memory Database
Below is a simple implementation for unit tests using SQLite In-Memory database:
public YourContext GetDbContext()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var option = new DbContextOptionsBuilder<YourContext>()
.UseSqlite(connection,
s => {
s.UseNetTopologySuite();
s.MigrationsHistoryTable("__MigrationHistory");
}).Options;
var dbContext = new YourContext(option);
//Added to recreate database and run migration for each test.
if (dbContext != null)
{
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
}
return dbContext;
}
And then in unit test:
var context = GetDbContext();
Alternatively, you can place the GetDbContext method in a fixture, so that you are only re-creating the database one time per test class. Have a dispose method in the fixture to run the dbContext.Database.EnsureDeleted() to clean up the data between test classes.
Shared context between Tests - Class Fixtures
com.mohendra.user
server
Application.class //Main class
package2
package3
domain
Campaigns.class
SmsDomainPackage.class
repository
CampaignRepository.class
The above is my folder structure, I am tryring to test CampaignRepository using spring dataJpaTest ,
I have written the following test
#ComponentScan(basePackages = "com.mohendra.user")
#EntityScan(basePackageClasses = SmsDomainPackage.class)
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#DataJpaTest
#RestClientTest
public class CampaignRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private CampaignRepository repository;
#Before
public void setUp() throws Exception {
}
#Test
public void findByCode() {
Campaigns campaigns = new Campaigns();
campaigns.setName("Name");
campaigns.setCode("HELP123");
campaigns.setStartDate(new Date());
campaigns.setEndDate(new Date());
this.entityManager.persist(campaigns);
Campaigns campaigns1 = repository.findByCode("HELP123");
System.out.println();
}
}
The test gives an exception of
java.lang.IllegalArgumentException: Unknown entity:
com.mohendra.user.package3.domain.Campaigns
I have also used #ComponentScan as you can see, and I've also used #EntityScan to try scan entities from packages, but both of them dont work.
I cannot change my folder structure to make it standard, as it is not my project. Is there a solution to it?
The Application class should be in the root package. That way you will not need any #CompontenScan or #EntityScan because Spring Boot scans everything below your root package
Therefor I recomment to put Application.class in the package com.mohendra.user
And you have to decide which test slice you want. You have three:
#SpringBootTest(classes = Application.class)
#DataJpaTest
#RestClientTest
But I assume that you only want #DataJpaTest
My REST API generates a password on a POST request and stores it in a DB. For development purpose, I'd like to put it in the response, when a development.mode flag is set to true in the application.properties file.
I would like to test this controller and check that if the flag is true, the password is in the response, if false the password is not.
I would like to know if and how I can dynamically set this property value from each test method.
This is my controller:
#RestController
public class PasswordController {
#Value("${development.mode}")
private boolean isDevelopmentMode;
#RequestMapping(value = "/new", method = RequestMethod.POST)
public ResponseEntity<String> generatePassword(#RequestParam(USERNAME_PARAM) String username) {
String password = manager.generatePassword();
String body = "";
if (isDevelopmentMode) {
body = "New password: " + password;
}
return new ResponseEntity<>(body, HttpStatus.OK);
}
}
I have seen documentation to define different property files for testing, using #PropertySource, but it's at the class level and I would like to avoid creating a different file just to change a flag.
I've also seen posts saying you can use ReflectionUtils.setField to change the value of a private field, but for this I have to explicity instantiate my controller and consequently have some issues with Autowired fields. I've tried this:
#RunWith(SpringRunner.class)
#WebMvcTest(PasswordControllerTest.class)
public class PasswordControllerTest extends OtpTest {
#Autowired
private MockMvc mockMvc;
#Test
public void newRoute_developmentModeIsFalse_responseBodyIsEmpty() throws Exception {
PasswordController controller = new PasswordController();
ReflectionTestUtils.setField(controller, "isDevelopmentMode", false);
MockMvc mvc = MockMvcBuilders.standaloneSetup(new PasswordController()).build();
given(manager.generatePassword(any(String.class))).willReturn("123456");
mvc.perform(post("/new").param(PasswordController.USERNAME_PARAM, "test_user")).andExpect(status().isOk()).andExpect(content().string(""));
}
}
Is there a possibility to set the value of a #Value field in each test method? Should I refactor my code to access it differently?
SOLUTION
Thanks to Borys Zibrov it works with this code:
#RunWith(SpringRunner.class)
#WebMvcTest(PasswordController.class)
public class PasswordControllerTest extends OtpTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext applicationContext;
#Test
public void newRoute_developmentModeIsTrue_passwordIsInTheResponseBody() throws Exception {
PasswordController controller = applicationContext.getBean(PasswordController.class);
ReflectionTestUtils.setField(controller, "isDevelopmentMode", true);
given(manager.generatePassword(any(String.class))).willReturn("123456");
mockMvc.perform(post("/new").param(PasswordController.USERNAME_PARAM, "test_user")).andExpect(status().isOk())
.andExpect(content().string("New password: 123456"));
}
}
Getting my controller bean from the application context didn't work at first because in my previous code I was using my test class in WebMvcTest annotation.
You could create another applicationContext-tests.xml, connect it to your test via:
#RunWith(SpringRunner.class)
#WebMvcTest(PasswordControllerTest.class)
#ContextHierarchy({
#ContextConfiguration(locations = "path_to applicationContext-tests.xml")
})
public class PasswordControllerTest extends OtpTest {
and override that property development.mode there.
You could also stick to using ReflectionTestUtils but instead of a direct instantiation use a bean from applicationContext constructed by spring for you, and then set a property on that bean.
Or you could just provide a setter for that field.
I am using the CakePHP-ReST-DataSource-Plugin Datasource for hitting a RESTful service in my model. This implies that the models will not have a database connection.
I have successfully accessed the services and would now like to write unit tests for the models. This is proving to be a daunting task since I cannot succeed to mock the datasource so that I do not hit the actual remote Service but rather return expected results for the tests.
<?php
App::uses('KnowledgePoint', 'Model');
class KnowledgePointTest extends CakeTestCase{
public $fixtures = array('app.knowledgepoint');
public $useDbConfig = 'RestTest';
private $KnowledgePoint;
public function setUp() {
parent::setUp();
$this->KnowledgePoint = ClassRegistry::init('KnowledgePoint');
/**
* This is the confusing part. How would I mock the datasource
so that I can mock the request method which returns the data
from the api?
*/
$this->KnowledgePoint->DataSource = $this->getMockForModel(
'RestSource',array('request'));
}
public function tearDown() {
parent::tearDown();
}
}
I would like to be able to mock the datasource and stub the request method to return data that would normally be returned from the remote service.
Kind regards,
Roland
Mocking the model and its getDataSource() method so that it returns your mocked datasource should theoretically work. Here's an example
App::uses('RestSource', 'Rest.Model/Datasource');
$DataSource = $this->getMock('RestSource', array('request'), array(array()));
$DataSource
->expects($this->any())
->method('request')
->will($this->returnValue('some custom return value'));
$Model = $this->getMockForModel('KnowledgePoint', array('getDataSource'));
$Model
->expects($this->any())
->method('getDataSource')
->will($this->returnValue($DataSource));
$Model->save(/* ... */);
In case you are wondering about the array(array()) for the datasource mock, this is required as the RestSource constructor doesn't supply a default value for the first argument (unlike the parent constructor).
When unit testing with NHibernate I will typically have tests that create and save an object, clear the session (session.Clear()) then retrieve the object from the database.
What's the equivalent of Session.Clear() with EF4?
Example test:
[Test]
public void Can_create_and_save_a_default_account()
{
var account = new Account();
_db.Accounts.AddObject(account);
_db.SaveChanges();
int id = account.AccountId;
// clear session
var fromDb = _db.Accounts.SingleOrDefault(x => x.AccountId == id);
Assert.IsNotNull(fromDb);
}
That will be recreating your DataContext-derived class (_db in your case).
You could mock your remote database with in-memory database. Here is example
SO after each test you will start from scratch.