-
Notifications
You must be signed in to change notification settings - Fork 865
Moved POCOs to EF and fixed tests #378
Conversation
Nah, its a bad idea to add EF dependencies, just copy the pocos into the shared test code and rename them for the tests |
In doing so, I have replicate the code in UserManagerTestBase for InMemory.Test project since it uses the IdentityUser object and that is a lot of duplication. Is that the right way ? |
There should be no replication other than copying the pocos from Core to the Shared Test code along side user manager test base with a new name for the pocos |
Ok let me see if the amount of duplicated code will be difficult to maintain in the future. I can reason out accordingly after that |
Now if I duplicate the POCOs in the Shared folder and make UserManagerTestBase use them, the SqlStoreBase complains since it uses UserManagerTestBase with IdentityUser from Identity.EF. If I make all the test projects to use the POCOs in the shared folder, then we need to maintain them along with the POCOs in the source. My concern is that if we forget to update them then the tests will pass incorrectly. |
Or maybe just move the IdentityUser classes to the new shared code stuff, so both EF and tests can depend on it safely. |
Another route could be to look at what the tests are doing to access the pocos directly and instead replace them with calls to identity, which might let you remove the IdentityUser constraint entirely |
Sure the second option might be an easy option. Let me try that out |
Updated with changes
Question
|
{ | ||
public static class IdentityServiceCollectionExtensions | ||
{ | ||
public static IdentityBuilder AddIdentity(this IServiceCollection services) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting that these overload now moves here because the typed moved and hence they are tied to EF. I think it would probably be better to get rid of them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed and updated
Also one important feedback i need to getting username synchronously inside a Linq queries. For example The workaround is to have a sync method on the |
/// <summary> | ||
/// Navigation property for users in the role | ||
/// </summary> | ||
public virtual ICollection<IdentityUserRole<TKey>> Roles { get; private set; } = new List<IdentityUserRole<TKey>>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will private
create issues with reflection on Mono?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need navigation properties on the shared test pocos?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True but I wanted to have parity with the Pocos in the source. The navigation property can be deleted in test pocos
@divega for the private thing, the issue is fixed in the latest version of Mono. But should we revert it to protected ? Do we foresee users overriding this property considering it is a navigation property ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its not about parity, the EF pocos are EF specific, if the navigation properties are not needed for the tests, they shouldn't be there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so the navigation properties are needed since the InMemory tests makes use of them. I need to keep them
Can't you do this? var userName = await mgr.GetUserNameAsync(u);
Assert.Null(mgr.Users.FirstOrDefault(u => userName == "bogus")); |
I don't understand this question, and I couldn't find the method right now. Did you already remove it? Were these test be independent of the provider? |
@@ -65,6 +67,152 @@ public void DropDatabaseStart() | |||
IdentityResultAssert.IsSuccess(await userManager.DeleteAsync(user)); | |||
} | |||
|
|||
[Fact] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these new tests or copied tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are new ones I added to test if navigation properties work when used with Include
on dbContext
@divega Using a variable ! so obvious. To answer the second question, yes the class no longer exists so it is fine |
@divega Now I recall that I did not use the variable since i need to use the object inside the |
Ah, I missed that. Sorry. |
Updated with feedback
|
I'll take another look later today. |
You might want to rebase since I added some functionals in InMemory yesterday, but otherwise looks to me |
@divega pinging a reminder |
}); | ||
|
||
var userManager = builder.ApplicationServices.GetRequiredService<UserManager<IdentityUser>>(); | ||
var dbContext = builder.ApplicationServices.GetRequiredService<IdentityDbContext>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DbContext is IDisposable. Please use using
even if doesn't seem needed right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, scratch that. I see now that you don't control the lifetime of this one directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However this still doesn't seem right. When will the context get disposed? I assume you copied this pattern from other tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am assuming you are worried how the database is cleared ? There is a test DropDatabaseStart
that is marked with lowest priority that will delete the database after all the tests in the class have run. This class existed all the way long. I have only added more tests that's all. Regarding the object, since it is created in the scope of the test, then it should be disposed at the end of it. If adding a using statement for DBContext is the recommended practice then sure I can update that but will have to do it in other tests as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am more worried about the risk of tests leaking resources, e.g. connections, or even memory depending how (well) the DI container implementation works and what we do in the tests.
Rebased with latest and remove all instances of Also noop for IQueryable tests is the managers do not support them |
|
||
protected abstract TRole CreateTestRole(string roleName = ""); | ||
|
||
protected abstract Expression<Func<TUser, bool>> UserNamePredicate(string userName, bool contains = false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I would rather simplify the tests to just succeed with simple equality and get rid of the contains flag on both predicates.
- Contains is not the same as StartsWith.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than that, I think we are very close.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah Contains != StartsWith, should be StartsWith
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't simplify the tests to just do equality since we aren't cleaning up the database, so a lot of the tests use the same username prefix, like "test", and we only drop/recreate the database once per run. Rather than trying to manually maintain user name uniqueness, I went with the approach of randomizing the names instead by always jamming on a guid, and then doing StratsWith instead of strict equality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We were doing name + guid
so I presumed Contains would still be valid check instead of StartWith considering we are looking if part of the string is present. I'll revert back to StartsWith it is just that I have to now maintain the original randomized username and pass it exactly to the predicate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am ok with having StartsWith checks if we can't have just do equality. But let's then split this in 4 simple predicate methods without any flags: GetUserNameEqualsPredicate(string)
, GetUserNameStartsWithPredicate(string)
, GetRoleNameEqualsPredicate(string)
, GetRoleNameStartsWithPredicate(string)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the four predicates and restored the test to original assert statements
Updated with 2 additional predicates to compare StartsWith for user/role name
@ajcvickers made changes to EF for mapping. Should I rebase and send another PR to see if that works with these changes ? |
I rebased it to latest EF which had renaming for mapping in OnModel creating. I have updated the IdentityDbContext with the latest changes @ajcvickers can you please specifically review changes to IdentityDbContext |
} | ||
Assert.Equal(roles.Count, manager.Roles.Count(RoleNameStartsWithPredicate("CanQuerableRolesTest"))); | ||
var r1 = manager.Roles.FirstOrDefault(RoleNameStartsWithPredicate("CanQuerableRolesTest1")); | ||
Assert.Equal(roles[1], r1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a negative assert as in the Users test and standardize on the same test data generation pattern?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure I updated it and role name are done the same way as usernames.
IdentityDbContext changes look fine. |
to get it on beta4. We can address any outstanding test issues later. |
thanks to everybody for patiently reviewing this |
@HaoK I moved the POCOs to Identity.EF. The Identity.Test and Identity.InMemory.Test broke due to this change since they did not reference Identity.EF. Now we could either duplicate the code in the tests or reference the assembly and include the namespace. In addition the UserManagerTestBase is shared so that has to updated also. I choose to reference the assembly as this seems more cleaner. I made sure we reference only the POCOs and nothing else. Let me know if there is a better way of doing this.
/cc @divega
You can skip the nitpick feedback for this PR