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.
Related
I have a base class called BaseService, and i want to verify if method IsValid was called in my unit test.
public interface IBaseService
{
bool IsValid<Dto, DtoValidator>(Dto entityDto, DtoValidator validator) where DtoValidator : AbstractValidator<Dto>;
}
public class BaseService : IBaseService
{
protected readonly IMapper _mapper;
protected readonly INotificationService _notification;
public BaseService(
IMapper mapper,
INotificationService notification)
{
_mapper = mapper;
_notification = notification;
}
public virtual bool IsValid<Dto, DtoValidator>(Dto entityDto, DtoValidator validator) where DtoValidator : AbstractValidator<Dto>
{
var result = validator.Validate(entityDto);
foreach (var error in result.Errors)
{
_notification.Notify(error.ErrorMessage);
}
return result.IsValid;
}
}
Class that use BaseService , some parts of code was omited
public interface IAfiliadoService : IBaseService {}
public class AfiliadoService : BaseService, IAfiliadoService
{
public AfiliadoService(
IMapper mapper,
INotificationService notification) : base(mapper, notification)
{
_afiliadoRepository = afiliadoRepository;
_lojaRepository = lojaRepository;
}
public async Task<AfiliadoResponseDto> AddAsync(AddAfiliadoRequestDto request)
{
if (IsValid(request, new AddAfiliadoRequestDtoValidator()))
{
}
}
}
In my test project i created those classes
public class MockBaseService
{
public readonly Mock<IMapper> _mapper;
public readonly Mock<INotificationService> _notification;
public MockBaseService()
{
_mapper = new Mock<IMapper>();
_notification = new Mock<INotificationService>();
}
}
public class AfiliadoServiceTest : MockBaseService
{
private readonly IAfiliadoService _afiliadoService;
public AfiliadoServiceTest()
{
_afiliadoService = new AfiliadoService(
_mapper.Object,
_notification.Object);
}
[Fact]
public async Task AdicionarUsuarioComEmailQueJaExisteDeveNotificar()
{
var request = new AddAfiliadoRequestDto();
var result = await _afiliadoService.AddAsync(request);
Assert.Null(result);
_notification
.Verify(v => v.Notify(It.IsAny<string>()), Times.Once);
// Here i want verify if method IsValid was called Once, like the verification above
}
}
I tried some things but with no success.
Exemple with what i want.
var mock = new Mock<AfiliadoService>();
mock.Verify(v => v.IsValid(request, new AddAfiliadoRequestDtoValidator()), Times.Once);
var mock = new Mock<AfiliadoService>(_mapper.Object,
_notification.Object);
var result = await mock.Object.AddAsync(request);
mock.Verify(v => v.IsValid(request, new AddAfiliadoRequestDtoValidator()), Times.Once);
This last one i got the message
Message:
Moq.MockException :
Expected invocation on the mock once, but was 0 times: v => v.IsValid<AddAfiliadoRequestDto, AddAfiliadoRequestDtoValidator>(AddAfiliadoRequestDto, AddAfiliadoRequestDtoValidator)
Performed invocations:
Mock<AfiliadoService:1> (v):
BaseService.IsValid<AddAfiliadoRequestDto, AddAfiliadoRequestDtoValidator>(AddAfiliadoRequestDto, AddAfiliadoRequestDtoValidator)
What im doing wrong?
Im using .net core - 3.1
xunit - 2.4.1
Moq - 4.16.0
I am new to ReactiveUI and trying to test a view model that looks like this:
public interface IService
{
Task<SessionModel> GetData(string id);
}
/// Provides a group of schedulers available to be used
public interface ISchedulers
{
IScheduler Default { get; }
IScheduler Dispatcher { get; }
}
public class MyVm : ReactiveObject
{
IService service;
public MyVm(ISchedulers schedulers, IService service)
{
this.service = service;
this.session = this.WhenAnyValue(x => x.SessionId)
.SelectMany(SearchSession)
.ObserveOn(schedulers.Default)
.ToProperty(this, x => x.Session);
}
private async Task<SessionModel> SearchSession(string id)
{
return await this.service.GetData(id);
}
private string sessionId;
public string SessionId
{
get => sessionId;
set => this.RaiseAndSetIfChanged(ref sessionId, value);
}
readonly ObservableAsPropertyHelper<SessionModel> session;
public SessionModel Session
{
get { return session.Value; }
}
}
public class SessionModel { }
I'm mocking the service call to return dummy data, but not sure what I need to do with a TestScheduler in order to get the SelectMany to work.
Here's a test class that shows how i would create a test for the view model. The goal is to eventually be able to check that the model got set:
[TestClass]
public class MyVmTests
{
[TestMethod]
public void CreateClass
{
var subject = new MyVm(/*pass in mocks*/);
subject.SessionId="test";
Assert.IsNotNull(subject.Session);
}
}
I don't think using TestScheduler is necessary. The following passes for me (using Moq):
var mockSchedulers = new Mock<ISchedulers>();
mockSchedulers.Setup(s => s.Default).Returns(Scheduler.Immediate);
var id = "123";
var mockService = new Mock<IService>();
var returnSession = new SessionModel();
mockService.Setup(s => s.GetData(It.Is<string>(i => i == id)))
.ReturnsAsync(returnSession);
var target = new MyVm(mockSchedulers.Object, mockService.Object);
target.SessionId = id;
Assert.IsNotNull(target.Session);
Assert.AreEqual(returnSession, target.Session);
TestScheduler is best when you're trying to test something with time (like a Delay, proving that the Delay actually happened). You're not really doing that here.
I have this simple Saga in Rebus:
public void MySaga : Saga<MySagaData>
IAmInitiatedBy<Event1>
IHandleMessages<Event2>
{
private IBus bus;
private ILog logger;
public MySaga(IBus bus, ILog logger)
{
if (bus == null) throw new ArgumentNullException("bus");
if (logger == null) throw new ArgumentNullException("logger");
this.bus = bus;
this.logger = logger;
}
protected override void CorrelateMessages(ICorrelationConfig<MySagaData> config)
{
config.Correlate<Event>(m => m.MyObjectId.Id, s => s.Id);
config.Correlate<Event>(m => m.MyObjectId.Id, s => s.Id);
}
public Task Handle(Event1 message)
{
return Task.Run(() =>
{
this.Data.Id = message.MyObjectId.Id;
this.Data.State = MyEnumSagaData.Step1;
var cmd = new ResponseCommandToEvent1(message.MyObjectId);
bus.Send(cmd);
});
}
public Task Handle(Event2 message)
{
return Task.Run(() =>
{
this.Data.State = MyEnumSagaData.Step2;
var cmd = new ResponseCommandToEvent2(message.MyObjectId);
bus.Send(cmd);
});
}
}
and thanks to the kind mookid8000 I can test the saga using FakeBus and a SagaFixture:
[TestInitialize]
public void TestInitialize()
{
var log = new Mock<ILog>();
bus = new FakeBus();
fixture = SagaFixture.For<MySaga>(() => new MySaga(bus, log.Object));
idTest = new MyObjectId(Guid.Parse("1B2E7286-97E5-4978-B5B0-D288D71AD670"));
}
[TestMethod]
public void TestIAmInitiatedBy()
{
evt = new Event1(idTest);
fixture.Deliver(evt);
var testableFixture = fixture.Data.OfType<MySagaData>().First();
Assert.AreEqual(MyEnumSagaData.Step1, testableFixture.State);
// ... more asserts
}
[TestMethod]
public void TestIHandleMessages()
{
evt = new Event2(idTest);
fixture.Deliver(evt);
var testableFixture = fixture.Data.OfType<MySagaData>().First();
Assert.AreEqual(MyEnumSagaData.Step2, testableFixture.State);
// ... more asserts
}
[TestCleanup]
public void TestCleanup()
{
fixture.Dispose();
bus.Dispose();
}
The first test method that check IAmInitiatedBy is correctly executed and no error is thrown, while the second test fail. It looks like a correlation issues since fixture.Data contains no elements and in fixture.LogEvents contains as last elements this error: Could not find existing saga data for message Event2/b91d161b-eb1b-419d-9576-2c13cd9d9c51.
What is this GUID? Is completly different from the one I defined in the unit test? Any ideas? Is legal what I'm tryng to test (since I'm using an in-memory bus)?
This line is bad: this.Data.Id = message.MyObjectId.Id. If you checked the value of Data.Id before you overwrote it, you would have noticed that the property already had a value.
You do not assign the saga ID - Rebus does that. And you should leave that property alone :)
Regarding your error - when Rebus wants to log information about a specific message, it logs a short name for the type and the message ID, i.e. the value of the automatically-assigned rbs2-msg-id header. In other words: It's not the value of the property m.MyObjectId.Id, you're seeing, it's the message ID.
Since the saga fixture is re-initialized for every test run, and you only deliver an Event2 to it (which is not allowed to initiate a new instance), the saga will not be hit.
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 am trying to setup database unit tests using the Sqllite.InMemory functionality.
If I run an unit test, everything is working fine. If I run the same test a second time I get an System.Data.SQLite.SQLiteException: no such table: Person
After waiting for a while and/or (?) restarting Visual studio I can run the unit test once again.
Is there something wrong with the configuration or the session handling?
public abstract class InMemoryDatabaseFixture : IDisposable
{
private const string ConnectionString
= "Data Source=:memory:;Version=3;New=True;Pooling=True;Max Pool Size=1;";
private readonly ISessionFactory _sessionFactory;
private readonly ISession _session;
protected InMemoryDatabaseFixture()
{
var config = SQLiteConfiguration.Standard.InMemory().ShowSql().ConnectionString(ConnectionString);
_sessionFactory = Fluently.Configure()
.Database(config)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionContainer>())
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))
.BuildSessionFactory();
_session = _sessionFactory.OpenSession();
SessionContainer = MockRepository.GenerateMock<ISessionContainer>();
SessionContainer.Stub(sc => sc.SessionFactory).Return(_sessionFactory);
SessionContainer.Stub(sc => sc.Session).Return(_session);
}
protected ISessionContainer SessionContainer { get; private set; }
public void Dispose()
{
_sessionFactory.Dispose();
_session.Dispose();
}
}
Here a simple usage of the base class:
[TestFixture]
public class FinderFixture : InMemoryDatabaseFixture
{
[Test]
public void Test()
{
var finder = new Finder(SessionContainer);
var result = finder.Find();
Assert.That(result, Is.Not.Null);
}
}
Update: After some trials here is finally my working configuration. Exporting the schema after building the SessionFactory is doing the magic.
Configuration configuration = null;
_sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql())
.ExposeConfiguration(cfg => configuration = cfg)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionContainer>())
.BuildSessionFactory();
_session = _sessionFactory.OpenSession();
var export = new SchemaExport(configuration);
export.Execute(true, true, false, _session.Connection, null);
You have requested that connection pooling in the ADO.NET provider be enabled. This will keep the underlying connection active even after NHibernate has closed it.
In my own unit tests, I have simply (based on your original code):
_sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionContainer>())
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))
.BuildSessionFactory();
Also, since the session is generated from the session factory, it would be prudent to dispose all sessions before disposing the session factory.