I have to test a client in my app which will further be commnicating to a third party rest Api. As of now I would like to Mock or fake that Api in my Unit Testing project. Does ABP provides integration to any mock framework like Moq. I specifically want to Unit Test API Calls. How can I mock API calls in abp framework for Unit Testing using xUnit/Moq
- ABP Framework version: v3.0.4
- UI type: Angular
- DB provider: EF Core
- Tiered (MVC) or Identity Server Separated (Angular): yes
11 Answer(s)
-
0
Hi Yaduraj,
ABP Framework provides NSubstitute for mocking in testing.
See an example below:
public class BookStoreTest : BookStoreApplicationTestBase { private ICurrentUser currentUser; protected override void AfterAddApplication(IServiceCollection services) { // Create a Substitute and replace original one in Service Collection currentUser = Substitute.For<ICurrentUser>(); services.AddSingleton(currentUser); } [Fact] public async Task ShouldCurrentUserBeOwnerOfBook() { var userId = Guid.NewGuid(); var username = "john.doe"; // You can configure return values of Properties or Methods currentUser.Id.Returns(userId); currentUser.UserName.Returns(username); // Just a check if the id same. Also all services in the same IoC container will use that configuration. var id = currentUser.Id; id.ShouldBe(userId); } }
I've just showed how to mock ICurrentUser, but you can mock any of your custom services that makes http call to a 3rd party API.
-
0
Thank you @enisn
-
0
Hi, This is urgent. We are facing some issue in mocking repositories using NSubstitute.
Can we connect through online session. Please help as soon as possible.
-
0
Hi @Yaduraj,
Can you share some information about the problem that you faced?
Otherwise I have to redirect to NSubstitute Documentation
-
0
Hi @enisn,
Thanks for the quick response.
We are using integration tests as in https://docs.abp.io/en/abp/latest/Testing#integration-tests So there are methods where we are calling a CustomAPIService that makes http call to a 3rd party API and would like to Substitute/Mock those calls. In this configuration if I mock the Repositories, I get SQLite Error 19: 'FOREIGN KEY constraint failed'.. Can't we mix NSubstitute with Integration Tests?
I request you to have an online session so that I can walk you through the code structure in better way. Becuase its a bit hard to give example as some of AppService contains around 30 dependencies
It will be really helpful. Thanks in Advance!
-
0
Can you share your project of part of project with us and we can see problem.
You can send it to info@abp.io with issue number (#1208) then we'll reproduce problem & see what's wrong.
-
0
Hi @enisn
The project is very large and could not be send over mail. It would be really great to have an online session where I can show you the code. Please can you guide my how can I get assisted with abp support team, where I can show my code?
-
0
Hi @Yaduraj
Can you provide a simple scenario to reproduce problem? I think problem occurs according to your dependencies, For example, you've mocked a Repository but it doesn't return foreign key id or mocked id doesn't match with related entity's id.
Otherwise, in integration test, you shouldn't mock repositories, you should use already seeded datas for testing. Let me introduce a full example that would explain what I meant:
Think there is a PurchaseAppService but this service uses IPaymentService to call a 3rd party API and make payment. Following example shows how to mock 3rd Party PaymentService
- A couple of DTOs:
public class PaymentResult { public string Code { get; set; } public bool Succeeded { get; set; } } public class PaymentInput { public decimal Price { get; set; } public string SomePaymentInfo { get; set; } } public class PurchaseInput { public Guid ProductId { get; set; } public int Amount { get; set; } }
- And interfaces:
public interface IPaymentService : ITransientDependency { Task<PaymentResult> MakePaymentAsync(PaymentInput input); } public interface IPurchaseAppService : IApplicationService { Task PurchaseAsync(PurchaseInput input); }
- See the implementation of PurchaseAppService, that calls a 3rd party service over its interface.
public class PurchaseAppService : ApplicationService, IPurchaseAppService { protected IPaymentService PaymentService { get; } protected IRepository<Product> ProductRepository { get; } public PurchaseAppService( IPaymentService paymentService, IRepository<Product> productRepository) { PaymentService = paymentService; ProductRepository = productRepository; } public async Task PurchaseAsync(PurchaseInput input) { var product = await ProductRepository.GetAsync(x => x.Id == input.ProductId); // 3rd Party Service Call var paymentResult = await PaymentService.MakePaymentAsync(new PaymentInput { Price = product.Price * input.Amount, SomePaymentInfo = "Some additrional data here..." }); if (!paymentResult.Succeeded) { throw new BusinessException(message: "Payment is failed..."); } // ... // There might be an insert code into Purchases table... } }
- Tests: In that case, I've already seeded a product with id Guid.Empty
public class PurchaseAppServiceTests : ProjectFApplicationTestBase { private readonly IPurchaseAppService purchaseAppService; private IPaymentService paymentService; public PurchaseAppServiceTests() { purchaseAppService = GetRequiredService<IPurchaseAppService>(); } protected override void AfterAddApplication(IServiceCollection services) { paymentService = Substitute.For<IPaymentService>(); services.AddSingleton(paymentService); } [Fact] public async Task MakePayment_Should_Be_Failed() { var paymentInput = new PaymentInput { Price = 10, SomePaymentInfo = "..." }; paymentService .MakePaymentAsync(paymentInput) .Returns(Task.FromResult(new PaymentResult { Code = "9901", Succeeded = false })); await Should.ThrowAsync<BusinessException>(async () => await purchaseAppService.PurchaseAsync(new PurchaseInput { ProductId = Guid.Empty, Amount = 2 }) ); } [Fact] public async Task MakePayment_Should_Be_Succeeded() { var paymentInput = new PaymentInput { Price = 10, SomePaymentInfo = "..." }; paymentService .MakePaymentAsync(paymentInput) .Returns(Task.FromResult(new PaymentResult { Code = "0000", Succeeded = true })); await Should.NotThrowAsync(async () => await purchaseAppService.PurchaseAsync(new PurchaseInput { ProductId = Guid.Empty, Amount = 2 }) ); } }
That test scenario mocks IPaymentService methods with no problem.
I highly suggest to check your all injected services and to make sure your seeder seeds consistent data.
And if you still think something is wrong, Feel free to share your scenario with code blocks
-
1
- ABP Framework version: v3.0.4
- UI type: Angular
- DB provider: EF Core
- Tiered (MVC) or Identity Server Separated (Angular): yes
Hi,
I was trying to mock IdentityUserManager
Error :
Can not instantiate proxy of class: Volo.Abp.Identity.IdentityUserManager. Could not find a parameterless constructor.
StackTrace :
at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments) at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors) at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.CreateProxyUsingCastleProxyGenerator(Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments, IInterceptor[] interceptors, ProxyGenerationOptions proxyGenerationOptions) at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.GenerateTypeProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments) at NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, Object[] constructorArguments) at NSubstitute.Core.SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments, Boolean callBaseByDefault) at NSubstitute.Core.SubstituteFactory.Create(Type[] typesToProxy, Object[] constructorArguments) at NSubstitute.Substitute.For(Type[] typesToProxy, Object[] constructorArguments) at NSubstitute.Substitute.For[T](Object[] constructorArguments) at SCV.Litmus.LitmusIdentity.LitmusUserInfoApplicationTests.AfterAddApplication(IServiceCollection services) in D:\Litmus\Projects\ap-ar-dashboard\SCV.Litmus\aspnet-core\modules\litmus-core\test\SCV.Litmus.Application.Tests\LitmusIdentity\LitmusUserInfoApplicationTests.cs:line 26 at Volo.Abp.Testing.AbpIntegratedTest`1..ctor() at SCV.Litmus.LitmusTestBase`1..ctor() at SCV.Litmus.LitmusApplicationTestBase..ctor() at SCV.Litmus.LitmusIdentity.LitmusUserInfoApplicationTests..ctor() in D:\Litmus\Projects\ap-ar-dashboard\SCV.Litmus\aspnet-core\modules\litmus-core\test\SCV.Litmus.Application.Tests\LitmusIdentity\LitmusUserInfoApplicationTests.cs:line 19
My Code :
public class LitmusUserInfoApplicationTests : LitmusApplicationTestBase { private readonly ILitmusUserInfoAppService _litmusUserInfoAppService; private IdentityUserManager _identityUserManager; public LitmusUserInfoApplicationTests() { _litmusUserInfoAppService = GetRequiredService<ILitmusUserInfoAppService>(); } protected override void AfterAddApplication(IServiceCollection services) { _identityUserManager = Substitute.For<IdentityUserManager>(); services.AddSingleton(_identityUserManager); } [Fact] public async Task GetSellerQuestionnaireForMakerCheckAsync() { _identityUserManager.FindByIdAsync("39faa1bb-6509-6a7d-b17f-0deee2cf47db") .Returns(Task.FromResult(JsonConvert.DeserializeObject<IdentityUser>(@"{ 'TenantId': 'd1be844b-d3a2-031a-f036-39f5d4380239', 'UserName': 'Seller.Admin', 'NormalizedUserName': 'SELLER.ADMIN', 'Name': 'Seller', 'Surname': 'Admin', 'Email': 'seller.admin@ness.com', 'NormalizedEmail': 'LALIT.CHOUGULE@NESS.COM', 'EmailConfirmed': false, 'PasswordHash': 'AQAAAAEAACcQAAAAEOK/7B0qge7madNnCVW5zGiVxa7sgIYc7XIKR/wG+fuDFn2g28hDGmd/i34RV/Rc8Q==', 'SecurityStamp': 'CUICLIBP3N5Z7NV52SKWF2MHBRIGDEPZ', 'PhoneNumber': null, 'PhoneNumberConfirmed': false, 'TwoFactorEnabled': true, 'LockoutEnd': null, 'LockoutEnabled': true, 'AccessFailedCount': 0, 'Roles': [ { 'TenantId': 'd1be844b-d3a2-031a-f036-39f5d4380239', 'UserId': '39fd4aa9-8553-35c0-4312-0d7b5f01b810', 'RoleId': '39fc43a0-e722-2bc3-7d24-9cc6992ccd3a' } ], 'Claims': [], 'Logins': [], 'Tokens': [], 'OrganizationUnits': [], 'IsDeleted': false, 'DeleterId': null, 'DeletionTime': null, 'LastModificationTime': '2021-06-23T16:22:02.440744', 'LastModifierId': null, 'CreationTime': '2021-06-23T16:18:53.936857', 'CreatorId': '1c26f3a7-fa8f-dbaf-406b-39f5d443409f', 'ExtraProperties': { 'PasswordSetDate': '2021-09-21T10:50:14.4543311Z', 'HaveOptedMakerChecker': true }, 'ConcurrencyStamp': '5224e087605a4f498a9684556b5506dc', 'Id': '39fd4aa9-8553-35c0-4312-0d7b5f01b810' }"))); var mock_sellerRequesterUsers = @"[ { 'TenantId': 'd1be844b-d3a2-031a-f036-39f5d4380239', 'UserName': 'Seller.Requester', 'NormalizedUserName': 'SELLER.REQUESTER', 'Name': 'Seller', 'Surname': 'Requester', 'Email': 'Kaustubh.Kale@ness.com', 'NormalizedEmail': 'KAUSTUBH.KALE@NESS.COM', 'EmailConfirmed': false, 'PasswordHash': 'AQAAAAEAACcQAAAAEFCPsMT+tOATxKorhUkndGa1/k3SOjnx3+emm2sWafeYlVvcnDFnSeP0k5uXlgYJwA==', 'SecurityStamp': 'NUUY6ROI2NTZ7UBM2FBDHZBKP5UPXS7B', 'PhoneNumber': null, 'PhoneNumberConfirmed': false, 'TwoFactorEnabled': true, 'LockoutEnd': null, 'LockoutEnabled': true, 'AccessFailedCount': 0, 'Roles': null, 'Claims': null, 'Logins': null, 'Tokens': null, 'OrganizationUnits': null, 'IsDeleted': false, 'DeleterId': null, 'DeletionTime': null, 'LastModificationTime': '2021-06-23T16:30:40.180889', 'LastModifierId': '1c26f3a7-fa8f-dbaf-406b-39f5d443409f', 'CreationTime': '2021-06-23T16:30:29.23093', 'CreatorId': '1c26f3a7-fa8f-dbaf-406b-39f5d443409f', 'ExtraProperties': { 'PasswordSetDate': '2021-09-21T11:00:28.4022237Z', 'HaveOptedMakerChecker': false }, 'ConcurrencyStamp': '0b7b68128a81401fbc6fa3f91e36b3f3', 'Id': '39fd4ab4-2271-7518-4964-a131224f8919' } ]"; _identityUserManager.GetUsersInRoleAsync("SellerRequester") .Returns(Task.FromResult(JsonConvert.DeserializeObject<IList<IdentityUser>>(mock_sellerRequesterUsers))); var mock_sellerApproverUsers = @"[ { 'TenantId': 'd1be844b-d3a2-031a-f036-39f5d4380239', 'UserName': 'Seller.Approver', 'NormalizedUserName': 'SELLER.APPROVER', 'Name': 'Seller', 'Surname': 'Approver', 'Email': 'Yaduraj.Shakti@ness.com', 'NormalizedEmail': 'YADURAJ.SHAKTI@NESS.COM', 'EmailConfirmed': false, 'PasswordHash': 'AQAAAAEAACcQAAAAEJkl+HrzdX1HQPWcLTvhmgtiibs2F4pX4avyk4UxK6iHFSCa5Ca4/VyNxVgyY47xIA==', 'SecurityStamp': 'TUOGMEIA2RJSFMXCKARFLZO2XSK2QWCA', 'PhoneNumber': null, 'PhoneNumberConfirmed': false, 'TwoFactorEnabled': true, 'LockoutEnd': null, 'LockoutEnabled': true, 'AccessFailedCount': 0, 'Roles': null, 'Claims': null, 'Logins': null, 'Tokens': null, 'OrganizationUnits': null, 'IsDeleted': false, 'DeleterId': null, 'DeletionTime': null, 'LastModificationTime': '2021-06-23T16:29:45.804466', 'LastModifierId': '1c26f3a7-fa8f-dbaf-406b-39f5d443409f', 'CreationTime': '2021-06-23T16:29:33.925747', 'CreatorId': '1c26f3a7-fa8f-dbaf-406b-39f5d443409f', 'ExtraProperties': { 'PasswordSetDate': '2021-09-21T10:59:32.8141291Z', 'HaveOptedMakerChecker': false }, 'ConcurrencyStamp': '7d9f64db3fa74b2b827940360d2b5185', 'Id': '39fd4ab3-494d-9f88-56ac-e9b402130a4d' } ]"; _identityUserManager.GetUsersInRoleAsync("SellerApprover") .Returns(Task.FromResult(JsonConvert.DeserializeObject<IList<IdentityUser>>(mock_sellerApproverUsers))); var result = await _litmusUserInfoAppService.GetSellerQuestionnaireInfoAsync(); } }
How do I mock IdentityUserManager ?
-
0
hi lalitChougule
Please create a class that repleace
IdentityUserManager
in you test project instead of mock it.[Dependency(ReplaceServices = true)] [ExposeServices(typeof(IdentityUserManager))] public class MyIdentityUserManager : IdentityUserManager { public MyIdentityUserManager(IdentityUserStore store, IIdentityRoleRepository roleRepository, IIdentityUserRepository userRepository, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<IdentityUser> passwordHasher, IEnumerable<IUserValidator<IdentityUser>> userValidators, IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<IdentityUserManager> logger, ICancellationTokenProvider cancellationTokenProvider, IOrganizationUnitRepository organizationUnitRepository, ISettingProvider settingProvider) : base(store, roleRepository, userRepository, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger, cancellationTokenProvider, organizationUnitRepository, settingProvider) { } public override Task<IdentityUser> FindByIdAsync(string userId) { if (userId == "39faa1bb-6509-6a7d-b17f-0deee2cf47db") { return Task.FromResult(new IdentityUser( //...)) } return base.FindByIdAsync(userId); } }
-
0
This question has been automatically marked as stale because it has not had recent activity.