Skip to content

Commit

Permalink
Work/v1 (#4)
Browse files Browse the repository at this point in the history
* Adding unit tests

* Added unit tests and fixed issue with TypeExtensions
  • Loading branch information
erenken authored May 7, 2023
1 parent 90d0980 commit 93d8ab9
Show file tree
Hide file tree
Showing 11 changed files with 362 additions and 14 deletions.
9 changes: 9 additions & 0 deletions QueryPattern.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuerySample", "sample\Query
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "myNOC.Tests.EntityFramework.Query", "tests\myNOC.Tests.EntityFramework.Query\myNOC.Tests.EntityFramework.Query.csproj", "{71B8019E-AF04-49FF-965C-DC106A3FC7C9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{DFFF835B-547A-4D6F-B739-7CBAE2142AD2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{1ABF61F2-FD86-4BFF-A509-64D673DF9913}"
ProjectSection(SolutionItems) = preProject
.github\workflows\build-tests.yml = .github\workflows\build-tests.yml
.github\workflows\release.yml = .github\workflows\release.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -41,6 +49,7 @@ Global
{BD5A2D1A-6761-4F89-9573-406079505724} = {A506A935-71CF-4D25-A7C4-FBE112BCFFDD}
{06AA0485-2E8B-4592-970A-7EF12C6149C2} = {2F1180C1-B4C8-4A4F-A348-68D90ABD7787}
{71B8019E-AF04-49FF-965C-DC106A3FC7C9} = {CFA418EB-F1B2-4BAF-A725-31EBAD0DC1D7}
{1ABF61F2-FD86-4BFF-A509-64D673DF9913} = {DFFF835B-547A-4D6F-B739-7CBAE2142AD2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {43AABAA3-16E5-4467-9DAE-6388878E4C1C}
Expand Down
6 changes: 6 additions & 0 deletions QueryPattern.v3.ncrunchsolution
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private static void GetServiceInterfaces(TypeInterfaces typeInterfaces, Type int
{
foreach(var it in GetServiceInterfaces(typeInterfaces.Type))
{
if (it.IsAssignableTo(interfaceType) && it != interfaceType)
if (it.IsAssignableTo(interfaceType))
typeInterfaces.Interfaces.Add(it);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/myNOC.EntityFramework.Query/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ Once you have your `queryRepo` you can execute the query.
var result = await queryRepo.Query(new ContactNameContains("a"));
```

`ContactNameContains` inherits from `IQueryList<ContactModal>` so the `Query` method will return `IEnumerable<ContactModel>` where the name contains an `a`.
`ContactNameContains` inherits from `IQueryList<ContactModel>` so the `Query` method will return `IEnumerable<ContactModel>` where the name contains an `a`.

You run a scalar query the same way.

Expand Down Expand Up @@ -218,4 +218,4 @@ static async Task SeedSampleData()
addressBook.Add(new ContactEntity { Id = 4, Name = "David" });
await addressBook.SaveChangesAsync();
}
```
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using myNOC.EntityFramework.Query.Extensions;

namespace myNOC.Tests.EntityFramework.Query.Extensions
{
[TestClass]
public class IEnumerableExtensionsTests
{
[TestMethod]
public void Apply_ExecutesAction_ReturnsIEnumerable()
{
// Assemble
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var total = numbers.Sum();
var calculated = 0;

// Act
var results = numbers.AsEnumerable().Apply(x => calculated += x);

// Assert
Assert.AreEqual(6, results.Count());
Assert.AreEqual(total, calculated);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Microsoft.Extensions.DependencyInjection;
using myNOC.EntityFramework.Query;
using myNOC.EntityFramework.Query.Extensions;

namespace myNOC.Tests.EntityFramework.Query.Extensions
{
[TestClass]
public class IServiceCollectionExtensionsTests
{
[TestMethod]
public void AddQueryPattern_ScanAppDomainForClassesThatInheritFromIQueryContextOrIQueryRepository_ScopedAndRegister()
{
// Assemble
var services = new ServiceCollection();

// Act
var results = services.AddQueryPattern();

// Assert
Assert.IsNotNull(results);
Assert.IsInstanceOfType(results, typeof(IServiceCollection));

Assert.IsNotNull(results.FirstOrDefault(x => x.ImplementationType == typeof(TestQueryContext)
&& x.ServiceType == typeof(IQueryContext)
&& x.Lifetime == ServiceLifetime.Scoped));

Assert.IsNotNull(results.FirstOrDefault(x => x.ImplementationType == typeof(TestQueryRepository)
&& x.ServiceType == typeof(IQueryRepository)
&& x.Lifetime == ServiceLifetime.Scoped));
}

internal class TestQueryContext : IQueryContext
{
public IQueryable<TEntity> Set<TEntity>() where TEntity : class
{
throw new NotImplementedException();
}
}

internal class TestQueryRepository : IQueryRepository
{
public Task<IEnumerable<TModel>> Query<TModel>(IQueryList<TModel> query) where TModel : class
{
throw new NotImplementedException();
}

public Task<TReturn?> Query<TReturn>(IQueryScalar<TReturn> query)
{
throw new NotImplementedException();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using myNOC.EntityFramework.Query.Extensions;
using System.Reflection;

namespace myNOC.Tests.EntityFramework.Query.Extensions
{
[TestClass]
public class TypeExtensionTests
{
private IEnumerable<Type> _types = default!;

[TestInitialize]
public void Initialize()
{
_types = Assembly.GetExecutingAssembly().GetTypes();
}

[TestMethod]
public void CanImplement_Types_ImplementsInterface_DirectInterface_ReturnsTypesThatInheritFromITestInterface2()
{
// Arrange
var implementedInterface = typeof(ITestInterface2);

// Act
var results = _types.CanImplement(implementedInterface);

// Assert
Assert.IsNotNull(results);
Assert.AreEqual(1, results.Count());
Assert.AreEqual(1, results[0].Interfaces.Count());
Assert.AreEqual(implementedInterface, results[0].Interfaces[0]);
Assert.AreEqual(typeof(TestClass3), results[0].Type);
Assert.IsNull(results.FirstOrDefault(x => x.Type == typeof(TestClass4)));
}

[TestMethod]
public void CanImplement_Types_ImplementsInterface_SupportsNestedInterfaces_ReturnsTypesThatInheritFromITestInterface()
{
// Arrange
var implementedInterface = typeof(ITestInterface);

// Act
var results = _types.CanImplement(implementedInterface);

// Assert
Assert.IsNotNull(results);
Assert.AreEqual(3, results.Count());
Assert.IsNotNull(results.FirstOrDefault(x => x.Type == typeof(TestClass1)));
Assert.IsNotNull(results.FirstOrDefault(x => x.Type == typeof(TestClass2)));
Assert.IsNotNull(results.FirstOrDefault(x => x.Type == typeof(TestClass5)));
Assert.IsNull(results.FirstOrDefault(x => x.Type == typeof(TestClass4)));
}

[TestMethod]
public void CanImplement_Types_ImplementsInterface_NestedClassesRecursion()
{
// Arrange
var implementedInterface = typeof(ITestInterface3);

// Act
var results = _types.CanImplement(implementedInterface);

// Assert
Assert.IsNotNull(results);
Assert.AreEqual(2, results.Count());
Assert.IsNotNull(results.FirstOrDefault(x => x.Type == typeof(TestClass6)));
Assert.IsNotNull(results.FirstOrDefault(x => x.Type == typeof(TestClass8)));
Assert.IsNull(results.FirstOrDefault(x => x.Type == typeof(TestClass4)));
}

[TestMethod]
public void CanImplement_Types_ImplementsInterface_SkipIDisposableInterfaceInGetInterfaces()
{
// Arrange
var implementedInterface = typeof(ITestInterface5);

// Act
var results = _types.CanImplement(implementedInterface);

// Assert
Assert.IsNotNull(results);
Assert.AreEqual(1, results.Count());
Assert.IsNotNull(results.FirstOrDefault(x => x.Type.Equals(typeof(TestClass7))));
Assert.IsNull(results.FirstOrDefault(x => x.Type == typeof(TestClass4)));
}

internal interface ITestInterface { }
internal interface ITestInterface2 { }
internal interface ITestInterface3 { }
internal interface ITestInterface4 : ITestInterface { }
internal interface ITestInterface5 : IDisposable { }
internal interface ITestInterface7 : ITestInterface3 { }

internal class TestClass1 : ITestInterface { }
internal class TestClass2 : ITestInterface { }
internal class TestClass3 : ITestInterface2 { }
internal class TestClass4 { }
internal class TestClass5 : ITestInterface4 { }
internal class TestClass6 : TestClass4, ITestInterface7 { }
internal class TestClass7 : ITestInterface5
{
public void Dispose()
{
throw new NotImplementedException();
}
}

internal class TestClass8 : TestClass6 { }
}
}
56 changes: 56 additions & 0 deletions tests/myNOC.Tests.EntityFramework.Query/QueryContextTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Microsoft.EntityFrameworkCore;
using myNOC.EntityFramework.Query;
using NSubstitute;
using System.ComponentModel.DataAnnotations;

namespace myNOC.Tests.EntityFramework.Query
{
[TestClass]
public class QueryContextTests
{
private TestContext _testDbContext = default!;
private QueryContext _queryContext = Substitute.ForPartsOf<QueryContext>();

[TestInitialize]
public void Initialize()
{
var options = new DbContextOptionsBuilder<TestContext>().UseInMemoryDatabase("testContext").Options;
_testDbContext = new TestContext(options);
}


[TestCleanup]
public void Cleanup()
{
_testDbContext?.Database?.EnsureDeleted();
_testDbContext?.Dispose();
}

[TestMethod]
public void Set_Returns_Entity()
{
// Assemble
_queryContext.GetContext().Returns(_testDbContext);

// Act
var result = _queryContext.Set<TestEntity>();

// Assert
Assert.IsNotNull(result);
Assert.IsInstanceOfType<IQueryable<TestEntity>>(result);
}

public class TestEntity
{
[Key]
public int Id { get; set; }
}

public class TestContext : DbContext
{
public DbSet<TestEntity> TestEntities { get; set; } = default!;

public TestContext(DbContextOptions options) : base(options) { }
}
}
}
96 changes: 96 additions & 0 deletions tests/myNOC.Tests.EntityFramework.Query/QueryRepositoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Microsoft.EntityFrameworkCore;
using myNOC.EntityFramework.Query;
using NSubstitute;
using System.ComponentModel.DataAnnotations;

namespace myNOC.Tests.EntityFramework.Query
{
[TestClass]
public class QueryRepositoryTests
{
private QueryContext _queryContext = default!;
private TestContext _testDbContext = default!;
private QueryRepository _queryRepository = default!;

[TestInitialize]
public void Initialize()
{
var options = new DbContextOptionsBuilder<TestContext>().UseInMemoryDatabase("testRepository").Options;
_testDbContext = new TestContext(options);
_testDbContext.TestEntities.Add(new TestEntity());
_testDbContext.TestEntities.Add(new TestEntity());
_testDbContext.TestEntities.Add(new TestEntity());
_testDbContext.SaveChanges();

_queryContext = Substitute.ForPartsOf<QueryContext>();
_queryContext.GetContext().Returns(_testDbContext);

_queryRepository = Substitute.ForPartsOf<QueryRepository>(_queryContext);
}

[TestCleanup]
public void Cleanup()
{
_testDbContext?.Database?.EnsureDeleted();
_testDbContext?.Dispose();
}

[TestMethod]
public async Task Query_RunAIQueryList_ReturnsIEnumerable()
{
// Assemble
var query = new TestEntitiesGetAll();

// Act
var result = await _queryRepository.Query(query);

// Assert
Assert.AreEqual(3, result.Count());
}

[TestMethod]
public async Task Query_RunAIQueryScalar_ReturnsInt()
{
// Assemble
var query = new TestEntitiesCount();

// Act
var result = await _queryRepository.Query(query);

// Assert
Assert.AreEqual(3, result);
}

public class TestModel { }

public class TestEntity
{
[Key]
public int Id { get; set; }
}

public class TestContext : DbContext
{
public DbSet<TestEntity> TestEntities { get; set; } = default!;

public TestContext(DbContextOptions options) : base(options) { }
}

internal class TestEntitiesGetAll : IQueryList<TestModel>
{
public IQueryable<TestModel> Query(IQueryContext context)
{
return from te in context.Set<TestEntity>()
select new TestModel();
}
}

internal class TestEntitiesCount : IQueryScalar<int>
{
public async Task<int> GetScalar(IQueryContext context)
{
return await context.Set<TestEntity>().CountAsync();
}
}
}
}
Loading

0 comments on commit 93d8ab9

Please sign in to comment.