I am getting an error while performing the database operation.
In my project when I try to access deleteAll() from Dao class ie.e,AuthenticationDaoof Room Database from my test cases I am getting below error
Refer to the below code:
#Dao
public interface AuthenticationDao {
#Insert
void insert(Authentication authentication);
#Query("delete from authentication")
void deleteAll();
#Query("select * from authentication")
Authentication getAuthInformation();
}
AppDatabase.java
#Database(entities = {Authentication.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public static AppDatabase getAppDatabase(Context context) {
if (INSTANCE == null) {
INSTANCE =
Room.databaseBuilder(context,
AppDatabase.class,
"my-database")
.allowMainThreadQueries()
.build();
}
return INSTANCE;
}
public abstract AuthenticationDao authenticationDao();
}
What is the issue?
Is it because I am using AppDatabase dbInstance = Room.databaseBuilder(context,AppDatabase.class,"my-database")
.allowMainThreadQueries()
.build(); to initialize my database object?
If I use AppDatabase dbInstance = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(), AppDatabase.class).allowMainThreadQueries().build(); then it works fine.
What could be the reason?
Related
I am trying to create the simple web api test for the controller action method I have in my project. I already create and add the test project in my solution. And add the Nunit nuget package in test project.
The controller I am trying to test is look like this:
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly IHostEnvironment _hostEnvironment;
private readonly ILogger<HomeController> _logger;
private BaseDataAccess _datatAccess = new BaseDataAccess()
public HomeController(ILogger<HomeController> logger, IConfiguration configuration, IHostEnvironment hostEnvironment)
{
_logger = logger;
_configuration = configuration;
_hostEnvironment = hostEnvironment;
}
[HttpGet("GetInfo/{code}")]
public IActionResult GetInfo(string code)
{
List<InfoModel> infos = new List<InfoModel>();
int isNumber;
if (String.IsNullOrEmpty(code) || !int.TryParse(code, out isNumber))
{
_logger.LogInformation(String.Format("The code pass as arguments to api is : {0}", code));
return BadRequest("Invalid code");
}
try
{
_logger.LogDebug(1, "The code passed is" + code);
SqlConnection connection = _datatAccess.GetConnection(_configuration, _hostEnvironment);
string sql = string.Format ("SELECT * from table1 where code={0}", code);
DataTable dt = _datatAccess.ExecuteQuery(connection,CommandType.Text, sql);
if (dt != null && dt.Rows.Count > 0)
{
foreach (DataRow dr in dt.Rows)
{
infos.Add(new InfoModel
{
ID = dr["id"].ToString(),
code = dr["code"].ToString()
});
}
}
}
catch (Exception ex)
{
_logger.LogError(4, String.Format("Error Message: " + ex.Message + "\n" + ex.StackTrace));
return BadRequest("There is something wrong.Please contact the administration.");
}
return new OkObjectResult(infos);
}
}
Now when I try to create the unit test I need to pass the configuration, hostenvironment and logger to HomeController from my TestHomeController. And I don't know how to instantiate these settings and pass to controller:
using NUnit.Framework;
using Microsoft.AspNetCore.Mvc;
using MyApi.Models;
using MyApi.Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MyApi.Tests
{
[TestFixture]
public class TestHomeController: ControllerBase
{
private readonly IConfiguration _configuration; //How to instantiate this so it is not null
private readonly IHostEnvironment _hostEnvironment ;//How to instantiate this so it is not null
private ILogger<HomeController> _logger;//How to instantiate this so it is not null
[Test]
public void GetInfo_ShouldReturnAllInfo()
{
var controller = new HomeConteoller(_logger, _configuration, _hostEnvironment);
var result = controller.GetInfo("11");
var okObjectResult = (OkObjectResult)result;
//Assert
okObjectResult.StatusCode.Equals(200);
}
}
}
Thanks for any help and suggestions.
Probably, you have startup.cs. Don't you?
if you gonna test a controller, then you need to build a whole instance of an application. Here I put an example of how you can test your code if you have Startup.cs.
public class SUTFactory : WebApplicationFactory<Startup>
{
protected override IHostBuilder CreateHostBuilder()
{
return Program.CreateHostBuilder(null);
}
}
public class TestControllerTests
{
private SUTFactory factory;
private HttpClient _client;
public TestControllerTests()
{
factory = new SUTFactory();
_client = factory.CreateClient();
}
[Test]
public async Task GetPatientInterviewID_ShouldReturnAllInterviewID()
{
// Arrange
var id = "11";
// Act
var result = await _client.GetAsync($"Home/GetInfo/{id}");
// Assert
Assert.AreEqual(System.Net.HttpStatusCode.OK, result.StatusCode);
}
}
This example is closer to Integration testing rather than Unit-testing. If you want to have unit-test then you need to do the following things
BaseDataAccess _datatAccess this is a specific realization and it cannot be mocked (comparing to ILogger, IHostEnvironment etc)
move all your code from the controller to a separate class, and test this class.
I have a repository
public class StudentsPersonalDetailsRepository : IStudentPersonalDetailsRepository
{
private readonly StudentManagementSystemEntities _studentsDbContext;
private readonly ILogger _logger;
public StudentsPersonalDetailsRepository(StudentManagementSystemEntities context, ILogger<IStudentPersonalDetailsRepository> logger)
{
_studentsDbContext = context;
_logger = logger;
}
public IQueryable<StudentPersonalDetails> StudentPersonalDetails => _studentsDbContext.StudentPersonalDetails;
......
}
In my Service layer, I am having a service as
public class StudentsPersonalDetailsService:IStudentPersonalDetailsService
{
private readonly IStudentPersonalDetailsRepository _repository;
private readonly ILogger _logger;
public StudentsPersonalDetailsService(IStudentPersonalDetailsRepository studentPersonalDetailsRepository,ILogger<StudentsPersonalDetailsService> logger)
{
_repository = studentPersonalDetailsRepository;
_logger = logger;
}
......
......
public StudentModelResponse GetStudentById(int id)
{
Domain.Entities.StudentPersonalDetails obj = _repository.StudentPersonalDetails.
Where(i => i.RollNo == id)
.Select(i=>new Domain.Entities.StudentPersonalDetails {
RollNo=i.RollNo,
FirstName=i.FirstName,
LastName=i.LastName,
MailId=i.MailId,
MiddleName=i.MiddleName,
DateOfBirth=i.DateOfBirth,
GenderOfPerson=i.GenderOfPerson
}).FirstOrDefault();
StudentModel ob = StudentModel.Translator(obj);
return new StudentModelResponse { StudentModel=ob};
}
}
My Test code is
namespace StudentUnitTests
{
public class StudentServiceShould
{
[Theory]
[InlineData(1)]
public void AbleToRetrieveStudentById(int n)
{
var mock = new Mock<IStudentPersonalDetailsRepository>();
var logger = new Mock<ILogger<StudentsPersonalDetailsService>> ();
var ob = new StudentsPersonalDetailsService(mock.Object, logger.Object);
}
}
}
I need to write a unit test for GetStudentById() and check the values returned by the function.
Please help me to how to mock the service layer.
In the above we have two things happening within StudentsPersonalDetailsService.GetStudentById()
Retrieve the student info from the repository.
Create a student model from the data retrieved from the repository
Note: Something looks strange when reading from the repository. If the items in the repository are StudentPersonalDetails why create new instances
We can stub retrieving the student data like so
public class StudentServiceShould
{
[Theory]
[InlineData(1)]
public void AbleToRetrieveStudentById(int n)
{
var students = new []{
// new Domain.Entities.StudentPersonalDetails for student role 1,
// new Domain.Entities.StudentPersonalDetails for student role 2,
// new Domain.Entities.StudentPersonalDetails for student role 3
};
var mock = new Mock<IStudentPersonalDetailsRepository>();
mock.SetupGet(mk => mk.StudentPersonalDetails).Returns(students.AsQueryable());
var logger = new Mock<ILogger<StudentsPersonalDetailsService>> ();
var ob = new StudentsPersonalDetailsService(mock.Object, logger.Object);
}
}
Creating the StudentModel objects is encapsulated in the Translator but because it is a static method on the 'StudentModel' we cannot mock it and will have to test the reading and conversion in one go.
I have build a WebAPI and apart from my tests running on Postman I would like to implement some Integration/Unit tests.
Now my business logic is very thin, most of the time its more of CRUD actions, therefore I wanted to start with testing my Controllers.
I have a basic setup. Repository pattern (interfaces), Services (business logic) and Controllers.
The flow goes Controller (DI Service) -> Service (DI Repo) -> Repo Action!
So what I did was override my Startup file to change into a in memory database and the rest should be fine (I would assume) Services are added, repos are added and now I am pointing into a in memory DB which is fine for my basic testing.
namespace API.UnitTests
{
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
: base(env)
{
}
public void ConfigureTestServices(IServiceCollection services)
{
base.ConfigureServices(services);
//services.Replace<IService, IMockedService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
base.Configure(app, env, loggerFactory);
}
public override void SetUpDataBase(IServiceCollection services)
{
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
services
.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
}
}
I wrote my first test, but the DatasourceService is not there:
The following constructor parameters did not have matching fixture data: DatasourceService datasourceService
namespace API.UnitTests
{
public class DatasourceControllerTest
{
private readonly DatasourceService _datasourceService;
public DatasourceControllerTest(DatasourceService datasourceService)
{
_datasourceService = datasourceService;
}
[Xunit.Theory,
InlineData(1)]
public void GetAll(int companyFk) {
Assert.NotEmpty(_datasourceService.GetAll(companyFk));
}
}
}
What am I missing?
You can't use dependency injection on test classes. You can only let xunit inject special fixtures via constructor (see docs).
For Integration Testing you want to use the TestServer class from Microsoft.AspNetCore.TestHost package and a separate Startup.cs class (easier to setup configuration than inheritance imho).
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureTestServices(IServiceCollection services)
{
services.Replace(ServiceDescriptor.Scoped<IService, MockedService>());
services.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
public void Configure(IApplicationBuilder app)
{
// your usual registrations there
}
}
In your unit test project, you need to create an instance of the TestServer and perform the test.
public class DatasourceControllerTest
{
private readonly TestServer _server;
private readonly HttpClient _client;
public DatasourceControllerTest()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<TestStartup>());
_client = _server.CreateClient();
}
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.GetAsync($"/api/datasource/{companyFk}");
// expected result from rest service
var expected = #"[{""data"":""value1"", ""data2"":""value2""}]";
// Assert
// This makes sure, you return a success http code back in case of 4xx status codes
// or exceptions (5xx codes) it throws an exception
response.EnsureSuccessStatusCode();
var resultString = await response.Content.ReadAsStringAsync();
Assert.Equals(resultString, expectedString);
}
}
Now, when you call operations which write to the database, you can also check if the data is really written to the database:
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.DeleteAsync($"/api/datasource/{companyFk}");
// expected result from rest service
// Assert
response.EnsureSuccessStatusCode();
// now check if its really gone in the database. For this you need an instance
// of the in memory Sqlite DB. TestServer has a property Host, which is an IWebHost
// and it has a property Services which is the IoC container
var provider = _server.Host.Services;
var dbContext = provider.GetRequiredService<ApplicationDbContext>();
var result = await dbContext.YourTable.Where(entity => entity.Id == companyFk).Any();
// if it was deleted, the query should result in false
Assert.False(result);
}
Now you can use Xunit.DependencyInjection in your tests.
namespace Your.Test.Project
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDependency, DependencyClass>();
}
}
}
your DI-classes:
public interface IDependency
{
int Value { get; }
}
internal class DependencyClass : IDependency
{
public int Value => 1;
}
and XUnit-test:
public class MyAwesomeTests
{
private readonly IDependency _d;
public MyAwesomeTests(IDependency d) => _d = d;
[Fact]
public void AssertThatWeDoStuff()
{
Assert.Equal(1, _d.Value);
}
}
I am trying to test the following Spring mvc controller method:
#RequestMapping(value = "/preferences/email", method = RequestMethod.POST, produces = "text/html")
public String modifyEmail(#ModelAttribute #Validated({ Validation.EmailModification.class }) EmailInfo emailInfo, BindingResult bindingResult, Model model, Locale locale) {
Member member = memberService.retrieveCurrentMember();
if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {
if (member.getEmail().equals(emailInfo.getEmail())) {
bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.same_email", null, locale)));
} else {
bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.email_already_used", null, locale)));
}
}
if (bindingResult.hasErrors()) {
model.addAttribute("emailInfo", emailInfo);
return "preferences";
}
preferencesService.modifyEmail(member, emailInfo.getEmail());
return "redirect:/preferences/email";
}
Here is the EmailInfo bean:
#RooEquals
#RooJavaBean
public class EmailInfo {
#NotNull(groups = { Validation.EmailModification.class })
#Pattern(regexp = "^[_a-z0-9-]+(\\.[_a-z0-9-]+)*#[a-z0-9-]+(\\.[a-z0-9-]+)+$", groups = { Validation.EmailModification.class })
private String email;
private boolean activated;
private String token;
}
Here is the test class:
#ContextConfiguration
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
public class PreferenceControllerTest {
#Autowired
private WebApplicationContext ctx;
private MockMvc mockMvc;
#Autowired
private MemberService memberService;
#Autowired
private PreferencesService preferencesService;
#Autowired
private MemberRepository memberRepository;
#Autowired
private SigninService signinService;
#Autowired
private MessageSource messageSource;
#Before
public void setup() {
mockMvc = webAppContextSetup(ctx).build();
Member currentMember = new Member();
currentMember.setEmail("currentMember#example.com");
when(memberService.retrieveCurrentMember()).thenReturn(currentMember);
when(preferencesService.isEmailAvailable("notAvailable#example.com")).thenReturn(Boolean.FALSE);
}
#Test
public void test() throws Exception {
mockMvc.perform(post("/preferences/email")//
.param("email", "newEmail#example.com"))//
.andDo(print()).andExpect(model().attributeHasNoErrors("emailInfo", "email"));
}
#Configuration
public static class testConfiguration {
#Bean
public PreferenceController preferenceController() {
return new PreferenceController();
}
#Bean
public PreferencesService preferenceService() {
return mock(PreferencesService.class);
}
#Bean
public MemberService memberService() {
return mock(MemberService.class);
}
#Bean
public MemberRepository memberRepository() {
return mock(MemberRepository.class);
}
#Bean
public SigninService signinService() {
return mock(SigninService.class);
}
#Bean
public MessageSource messageSource() {
return mock(MessageSource.class);
}
}
}
Curiously I get the following output:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /preferences/email
Parameters = {email=[newEmail#example.com]}
Headers = {}
Handler:
Type = com.bignibou.controller.PreferenceController
Async:
Was async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = preferences
View = null
Attribute = emailInfo
value = com.bignibou.controller.helpers.EmailInfo#9a56c123
errors = [Field error in object 'emailInfo' on field 'email': rejected value [null]; codes []; arguments []; default message [null]]
FlashMap:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = preferences
Redirected URL = null
Cookies = []
The test fails with the above output and I am not sure why. I expected the test to pass as the email address is available.
Can anyone please help?
edit 1:
The following is not working either:
#Before
public void setup() {
mockMvc = webAppContextSetup(ctx).build();
Member currentMember = new Member();
currentMember.setEmail("currentMember#example.com");
when(memberService.retrieveCurrentMember()).thenReturn(currentMember);
when(preferencesService.isEmailAvailable(eq("notAvailable#example.com"))).thenReturn(Boolean.FALSE);
when(preferencesService.isEmailAvailable(eq("newEmail#example.com"))).thenReturn(Boolean.TRUE);
}
edit 2:
I was able to get is to work with the above edit 1 plus the test below:
#Test
public void test() throws Exception {
mockMvc.perform(post("/preferences/email")//
.param("email", "available#example.com"))//
.andDo(print())//
.andExpect(model().attributeHasNoErrors("emailInfo"));
}
With this :
.param("email", "newEmail#example.com"))//
You are setting request parameter to the string value. However you have not shown your conversion from String to EmailInfo.
In your test you are checking the field of emailInfo called email.
I am not sure what this is for ?
when(preferencesService.isEmailAvailable("notAvailable#example.com")).thenReturn(Boolean.FALSE);
What is supposed to do, you have injected your preferenceService using autowired.
Updae to answer comment.
in your controller try
String email=emailInfo.getEmail();
if(!preferencesService.isEmailAvailable(email))){ instead of if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {
Not sure, just a possible solution
Or try
when(preferencesService.isEmailAvailable(eq("newEmail#example.com"))).thenReturn(Boolean.TRUE);
when(preferencesService.isEmailAvailable(eq("notAvailable#example.com"))).thenReturn(Boolean.FALSE);
Ae you using Mockito to implement mocking?
I am not 100% sure but here is How I understand your code.
when(preferencesService.isEmailAvailable("notAvailable#example.com")).thenReturn(Boolean.FALSE);
if preferencesService.isEmailAvailable returns true then you are forcefully returning false in mock exercise
so when in mock exercise preferencesService.isEmailAvailable will always return false.
Now in your Controller
if (!preferencesService.isEmailAvailable(emailInfo.getEmail())) {
if (member.getEmail().equals(emailInfo.getEmail())) {
bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.same_email", null, locale)));
} else {
bindingResult.addError(new FieldError("emailInfo", "email", messageSource.getMessage("controller.preferences.email_already_used", null, locale)));
}
}
If preferencesService.isEmailAvailable is false then ! make it true so code will always go inside if Block , and you will get Field Error, and hence Test fails.
I downloaded Rhino Security today and started going through some of the tests. Several that run perfectly in isolation start getting errors after one that purposely raises an exception runs though. Here is that test:
[Test]
public void EntitiesGroup_IfDuplicateName_Error() {
_authorizationRepository.CreateEntitiesGroup("Admininstrators");
_session.Flush();
var ex = Assert.Throws<GenericADOException>(
() =>
{
_authorizationRepository.CreateEntitiesGroup("Admininstrators");
_session.Flush();
}).InnerException;
Assert.That(ex.Message, Is.StringContaining("unique"));
}
And here are the tests and error messages that fail:
[Test]
public void User_CanSave() {
var ayende = new User {Name = "ayende"};
_session.Save(ayende);
_session.Flush();
_session.Evict(ayende);
var fromDb = _session.Get<User>(ayende.Id);
Assert.That(fromDb, Is.Not.Null);
Assert.That(ayende.Name, Is.EqualTo(fromDb.Name));
}
----> System.Data.SQLite.SQLiteException : Abort due to constraint violation column Name is not unique
[Test]
public void UsersGroup_CanCreate()
{
var group = _authorizationRepository.CreateUsersGroup("Admininstrators");
_session.Flush();
_session.Evict(group);
var fromDb = _session.Get<UsersGroup>(group.Id);
Assert.NotNull(fromDb);
Assert.That(fromDb.Name, Is.EqualTo(group.Name));
}
failed: NHibernate.AssertionFailure : null id in Rhino.Security.Tests.User entry (don't flush the Session after an exception occurs)
Does anyone see how I can reset the state of the in memory SQLite db after the first test?
I changed the code to use nunit instead of xunit so maybe that is part of the problem here as well.
Cheers,
Berryl
This is the base class that instantiates the session
public abstract class DatabaseFixture : IDisposable
{
protected Account _account;
protected IAuthorizationRepository _authorizationRepository;
protected IAuthorizationService _authorizationService;
protected IPermissionsBuilderService _permissionsBuilderService;
protected IPermissionsService _permissionService;
protected User _user;
protected ISession _session;
protected readonly ISessionFactory _factory;
protected DatabaseFixture()
{
BeforeSetup();
SillyContainer.SessionProvider = (() => _session);
var sillyContainer = new SillyContainer();
ServiceLocator.SetLocatorProvider(() => sillyContainer);
Assert.NotNull(typeof(System.Data.SQLite.SQLiteConnection));
var cfg = new Configuration()
.SetProperty(Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
.SetProperty(Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionString, ConnectionString)
.SetProperty(Environment.ProxyFactoryFactoryClass, typeof(ProxyFactoryFactory).AssemblyQualifiedName)
.SetProperty(Environment.ReleaseConnections, "on_close")
.SetProperty(Environment.UseSecondLevelCache, "true")
.SetProperty(Environment.UseQueryCache, "true")
.SetProperty(Environment.CacheProvider,typeof(HashtableCacheProvider).AssemblyQualifiedName)
.AddAssembly(typeof (User).Assembly);
Security.Configure<User>(cfg, SecurityTableStructure.Prefix);
_factory = cfg.BuildSessionFactory();
_session = _factory.OpenSession();
new SchemaExport(cfg).Execute(false, true, false, _session.Connection, null);
_session.BeginTransaction();
SetupEntities();
_session.Flush();
}
protected virtual void BeforeSetup() { }
public virtual string ConnectionString { get { return "Data Source=:memory:"; } }
public void Dispose()
{
if (_session.Transaction.IsActive)
_session.Transaction.Rollback();
_session.Dispose();
}
private void SetupEntities()
{
_user = new User {Name = "Ayende"};
_account = new Account {Name = "south sand"};
_session.Save(_user);
_session.Save(_account);
_authorizationService = ServiceLocator.Current.GetInstance<IAuthorizationService>();
_permissionService = ServiceLocator.Current.GetInstance<IPermissionsService>();
_permissionsBuilderService = ServiceLocator.Current.GetInstance<IPermissionsBuilderService>();
_authorizationRepository = ServiceLocator.Current.GetInstance<IAuthorizationRepository>();
_authorizationRepository.CreateUsersGroup("Administrators");
_authorizationRepository.CreateEntitiesGroup("Important Accounts");
_authorizationRepository.CreateOperation("/Account/Edit");
_authorizationRepository.AssociateUserWith(_user, "Administrators");
_authorizationRepository.AssociateEntityWith(_account, "Important Accounts");
}
}
How are you instantiating the session?
Whenever there's an exception, the session must be discarded. That also means you should almost never share the session between test methods.