I try to make a test with xUnit finding the Id in a query in the controller.
My code its.
public class CuestionarioTest
{
public readonly CuestionarioController _controller;
private readonly Mock<ICuestionarioServicio> _cuestionariosServicio;
private readonly Mock<IRespuestaServicio> _respuestaServicio;
public CuestionarioTest()
{
_cuestionariosServicio = new Mock<ICuestionarioServicio>();
_respuestaServicio = new Mock<IRespuestaServicio>();
_controller = new CuestionarioController(_cuestionariosServicio.Object, _respuestaServicio.Object);
}
[Fact]
public async Task ComprobarBusquedaPorId()
{
int id = 1;
var result = await _controller.BuscarPorId(id);
//Assert.IsType<OkObjectResult>(result);
Assert.Contains(1, result.);
}
}
This is my method
public class CuestionarioController : Controller
{
private readonly ICuestionarioServicio _cuestionariosServicio;
private readonly IRespuestaServicio _respuestaServicio;
public CuestionarioController(ICuestionarioServicio cuestionarioServicio, IRespuestaServicio respuestaServicio)
{
_cuestionariosServicio = cuestionarioServicio;
_respuestaServicio = respuestaServicio;
}
public async Task<IActionResult> BuscarPorId(int id)
{
return Ok(await _cuestionariosServicio.ObtenerPorId(id));
}
I don't know how can validate if the result contain the Id with the result.
Please help.
If you cast the result, you can pull out the value:
var value = (result as OkObjectResult).Value;
Assert.Contains(1, value);
Related
I'm new at working on the abp.io framework, precisely Angular + Entity Framework Core.
I want to be able to create or display an objects list of a class that I've created.
For example, I've made a class called Address on the Domain layer.
Here is its AppService on the Application layer:
namespace ASKOM.RefPlusStudio.core.Services
{
[Authorize(corePermissions.Addresses.Default)]
public class AddressAppService : coreAppService, IAddressAppService
{
private readonly IAddressRepository _addressRepository;
private readonly AddressManager _addressManager;
public AddressAppService(IAddressRepository addressRepository, AddressManager addressManager)
{
_addressRepository = addressRepository;
_addressManager = addressManager;
}
[Authorize(corePermissions.Addresses.Create)]
public async Task<AddressDto> CreateAsync(CreateUpdateAddressDto input)
{
var address = await _addressManager.CreateAsync(
input.StreetNumber,
input.StreetName,
input.PostalCode,
input.City,
input.Country
);
await _addressRepository.InsertAsync(address);
return ObjectMapper.Map<Address, AddressDto>(address);
}
[Authorize(corePermissions.Addresses.Delete)]
public async Task DeleteAsync(Guid id)
{
await _addressRepository.DeleteAsync(id);
}
public async Task<AddressDto> GetAsync(Guid id)
{
var address = await _addressRepository.GetAsync(id);
return ObjectMapper.Map<Address, AddressDto>(address);
}
public async Task<PagedResultDto<AddressDto>> GetListAsync(GetAddressListDto input)
{
if (input.Sorting.IsNullOrWhiteSpace())
{
input.Sorting = nameof(Address.Country);
}
var addresses = await _addressRepository.GetListAsync(
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter
);
var totalCount = await AsyncExecuter.CountAsync(
_addressRepository.WhereIf(
!input.Filter.IsNullOrWhiteSpace(),
address => address.Country.Contains(input.Filter)
)
);
return new PagedResultDto<AddressDto>(
totalCount,
ObjectMapper.Map<List<Address>, List<AddressDto>>(addresses)
);
}
[Authorize(corePermissions.Addresses.Edit)]
public async Task UpdateAsync(Guid id, CreateUpdateAddressDto input)
{
var address = await _addressRepository.GetAsync(id);
address.StreetNumber = input.StreetNumber;
address.StreetName = input.StreetName;
address.PostalCode = input.PostalCode;
address.City = input.City;
address.Country = input.Country;
await _addressRepository.UpdateAsync(address);
}
}
}
I gave them all the permissions needed.
Here is corePermissions.cs :
namespace ASKOM.RefPlusStudio.core.Permissions
{
public static class corePermissions
{
public const string GroupName = "core";
//Add your own permission names. Example:
//public const string MyPermission1 = GroupName + ".MyPermission1";
public static class Addresses
{
public const string Default = GroupName + ".Addresses";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
}
}
I wanted to see if it can display an Addresses list so I've made a static one on DataSeeder:
namespace ASKOM.RefPlusStudio.core
{
public class coreDataSeederContributor : IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Address, Guid> _addressRepository;
private readonly IGuidGenerator _guidGenerator;
public coreDataSeederContributor(IRepository<Address, Guid> addressRepository, IGuidGenerator guidGenerator)
{
_addressRepository = addressRepository;
_guidGenerator = guidGenerator;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _addressRepository.GetCountAsync() > 0)
{
return;
}
var address = new Address(
id: _guidGenerator.Create(),
streetNumber: 07,
streetName: "Med Salah Belhaj",
postalCode: 2080,
city: "Ariana",
country: "Tunisie"
);
//autoSave: true
await _addressRepository.InsertAsync(address);
}
}
}
Here is now the result on Swagger UI:
When I try to open the request URL, here is what it shows:
I'm sure that I may have forgotten something and that's why I get Access Denied on the Request URL, but I don't really know what it is because I'm new at this.
Could you please help me?
Thank you
There was a problem with the database. That's why it hasn't read the data I've provided in the DataSeedProvider.
How would I go about modifying existing code to call other controllers stored dictionary information? (without recalling the db multiple times, but just once at the start of the rest api's life).
Atm (I think) am storing the information in a dictionary (PipeMaterials) correctly. Now I'm lost on how to go about getting the information out to other controller.
Controller storing information
Controller wanting to consume information
Storing
public class MaterialsController : ControllerBase
{
public Dictionary<int, Materials> PipeMaterials;
public Dictionary<int, Rank> Ranks;
public Dictionary<int, Sumps> Sumps;
private readonly UMMClient23Context _context;
public MaterialsController(UMMClient23Context context)
{
_context = context;
LoadMaterials();
}
public void LoadMaterials()
{
PipeMaterials = new Dictionary<int, Materials>();
Task<MaterialsObjects> task = GetMaterials();
var result = task.Result;
foreach (var item in result.Ummmaterials)
{
if (!PipeMaterials.TryAdd(item.MaterialsId, item))
{
Console.Error.WriteLine("Could not load material: " + item.MaterialsName);
}
}
}
// GET: api/Materials
[HttpGet]
public async Task<MaterialsObjects> GetMaterials()
{
MaterialsObjects returnable = new MaterialsObjects();
returnable.Ummmaterials = await _context.Materials.ToListAsync();
return returnable;
}
// GET: api/MaterialDescription/5
[HttpGet("{materialsId}")]
public string GetMaterialDescription(int materialsId)
{
Materials item;
if (PipeMaterials.TryGetValue(materialsId, out item))
{
return item.MaterialsName;
}
else
{
return null;
}
//var materials = _context.Materials
// .Where(m=> m.MaterialsId == materialsId)
// .Select(m => m.MaterialsName)
// .FirstOrDefault();
}
Consuming
public class PipeController : ControllerBase
{
MaterialsController materialsController;
UMMDBHelper uMMDBHelper;
private readonly UMMClient23Context _context;
public PipeController(UMMClient23Context context)
{
_context = context;
uMMDBHelper = new UMMDBHelper(context);
}
//GET: api/Pipe
[HttpGet]
public async Task<ActionResult<IEnumerable<Data>>> Get(string value)
{
return await _context.Data.ToListAsync();
}
// GET: api/Pipe/{assestNumber}
[HttpGet("{assetNumber}")] // Make return into Object
public PipeListObject GetPipe(string assetNumber)
{
PipeListObject returnable = new PipeListObject();
Pipe Pipe = uMMDBHelper.GetPipe(assetNumber);
returnable.UmmPipes.Add(Pipe);
return returnable;
}
//GET: api/PipeMaterial/{materialId}
[HttpGet("{materialId}")]
public string GetPipeMaterial(int materialId)
{
var desc = materialsController.GetMaterialDescription(materialId);
return desc;
}
You could create a new MaterialsController directly in PipeController like
var desc = new MaterialsController(_context).GetMaterialDescription(materialId);
Or you could use HttpClient like
[HttpGet("{materialId}")]
public string GetPipeMaterial(int materialId)
{
using (var client = new HttpClient())
{
var response = await client.GetAsync("https://localhost:44317/api/MaterialDescription/5");
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().Result;
return result;
}
}
return "";
}
I have about 15 entities for which I have to get id and name properties. First, I check whether the properties can be pulled from local cache. If not, then I fetch them by code from DB and save in cache.
Here's my code just for two entities:
public class GetParamsService : IGetParamsService
{
private readonly IMemoryCache _cache;
private readonly MemoryCacheEntryOptions _cacheOptions;
private readonly IDealTypeRepository _dealTypeRepository;
private readonly ICurrencyRepository _currencyRepository;
public GetParamsService(IMemoryCache memoryCache,
IDealTypeRepository dealTypeRepository,
ICurrencyRepository currencyRepository)
{
_cache = memoryCache;
_cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(2));
_dealTypeRepository = dealTypeRepository;
_currencyRepository = currencyRepository;
}
public async Task<(int idDealType, string dealTypeName)> GetDealTypeParams(
string dealTypeCode)
{
if (!_cache.TryGetValue(CacheKeys.IdDealType, out int idDealType)
| !_cache.TryGetValue(CacheKeys.DealTypeName, out string dealTypeName))
{
var dealType = await _dealTypeRepository
.Get(x => x.Code == dealTypeCode, dealTypeCode);
idDealType = dealType.IdDealType;
dealTypeName = dealType.Name;
_cache.Set(CacheKeys.IdDealType, idDealType, _cacheOptions);
_cache.Set(CacheKeys.DealTypeName, dealTypeName, _cacheOptions);
}
return (idDealType, dealTypeName);
}
public async Task<(int idCurrency, string currencyName)> GetCurrencyParams(
string currencyCode)
{
if (!_cache.TryGetValue(CacheKeys.IdCurrency, out int idCurrency)
| !_cache.TryGetValue(CacheKeys.CurrencyName, out string currencyName))
{
var currency = await _currencyRepository
.Get(x => x.Code == currencyCode, currencyCode);
idCurrency = currency.IdCurrency;
currencyName = currency.Name;
_cache.Set(CacheKeys.IdCurrency, idCurrency, _cacheOptions);
_cache.Set(CacheKeys.CurrencyName, currencyName, _cacheOptions);
}
return (idCurrency, currencyName);
}
}
So, the methods GetDealTypeParams and GetCurrencyParams are pretty much the same and I want to make one generic method instead of many similar methods. I guess it makes sense.
The problem is that I don't know how to get "the right properties for given entity" in CacheKeys class:
public static class CacheKeys
{
public static string IdDealType => "_IdDealType";
public static string DealTypeName => "_DealTypeName";
public static string IdCurrency => "_IdCurrency";
public static string CurrencyName => "_CurrencyName";
// ...
}
Every repository is inheritted from GenericRepository with Get method:
public class DealTypeRepository : GenericRepository<DealTypeEntity>, IDealTypeRepository
{
public DealTypeRepository(DbContextOptions<MyContext> dbContextOptions)
: base(dbContextOptions)
{
}
}
public class GenericRepository<TEntity> where TEntity : class
{
private readonly DbContextOptions<MyContext> _dbContextOptions;
public GenericRepository(DbContextOptions<MyContext> dbContextOptions)
{
_dbContextOptions = dbContextOptions;
}
public async Task<TEntity> Get(Expression<Func<TEntity, bool>> predicate, string code)
{
try
{
using (var db = new MyContext(_dbContextOptions))
{
using (var tr = db.Database.BeginTransaction(
IsolationLevel.ReadUncommitted))
{
var entity = await db.Set<TEntity>().AsNoTracking()
.FirstAsync(predicate);
tr.Commit();
return entity;
}
}
}
catch (Exception e)
{
throw new Exception("Error on getting entity by code: {code}");
}
}
}
Can you please guide me how can I retrieve the needed properties of CacheKeys class to write a generic method ? I guess it can be easily done with reflection.
UPDATE:
I'm not sure whether I have to try one generic method as every entity has Id property with its own name (for example, IdDealType for dealType, IdCurrency for currency)
Before I start: This solution assumes that all your entities follow the naming conventions you showed on your sample code.
First, it would be better if you had a repository exclusive to this service, where it would be possible to query by any entity type. There, you would use your convention to get those properties names, and you could query them by using EF.Property. As all your queries seem to be on the Code column, we can also simplify the parameters on that repo's method.
public class ParamRepository : IParamRepository
{
private readonly DbContextOptions<MyContext> _dbContextOptions;
public ParamRepository(DbContextOptions<MyContext> dbContextOptions)
{
_dbContextOptions = dbContextOptions;
}
public async Task<(int id, string name)> GetParamsByCode<TEntity>(string code) where TEntity : class
{
string entityName = typeof(TEntity).Name;
string idProp = $"Id{entityName}";
string nameProp = $"{entityName}Name";
try
{
using (var db = new MyContext(_dbContextOptions))
{
var entity = await db.Set<TEntity>().AsNoTracking()
.Where(p => EF.Property<string>(p, "Code") == code)
.Select(p => new { Id = EF.Property<int>(p, idProp), Name = EF.Property<string>(p, nameProp)})
.FirstAsync();
return (id: entity.Id, name: entity.Name);
}
}
catch (Exception e)
{
throw new Exception("Error on getting entity by code: {code}");
}
}
}
You would also need to refactor your cache keys to be created from conventions:
public static class CacheKeys
{
public static string GetIdKey<TEntity>() => $"_Id{typeof(TEntity).Name}";
public static string GetNameKey<TEntity>() => $"_{typeof(TEntity).Name}Name";
}
Then, it gets easy on the GetParamsService:
public class GetParamsService
{
private readonly IMemoryCache _cache;
private readonly MemoryCacheEntryOptions _cacheOptions;
private readonly IParamRepository _paramRepository;
public GetParamsService(IMemoryCache memoryCache,
IParamRepository paramRepository)
{
_cache = memoryCache;
_cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(2));
_paramRepository = paramRepository;
}
public async Task<(int id, string name)> GetParams<TEntity>(string code) where TEntity : class
{
string cacheIdKey = CacheKeys.GetIdKey<TEntity>();
string cacheNameKey = CacheKeys.GetNameKey<TEntity>();
if (!_cache.TryGetValue(cacheIdKey, out int cacheId)
| !_cache.TryGetValue(cacheNameKey, out string cacheName))
{
var param = await _paramRepository.GetParamsByCode<TEntity>(code);
cacheId = param.id;
cacheName = param.name;
_cache.Set(cacheIdKey, cacheId, _cacheOptions);
_cache.Set(cacheNameKey, cacheName, _cacheOptions);
}
return (cacheId, cacheName);
}
}
I can't get Moq to mock an object that gets created in a static method.
Here is my moq and code
code:
public interface IConfigHelper
{
string GetConfiguration(string sectionName, string elementName);
}
public class ConfigHelper : IConfigHelper
{
public ConfigHelper() { }
public virtual string GetConfiguration(string sectionName, string elementName)
{
string retValue = String.Empty;
//Does things to get configuration and return a value
return retValue;
}
}
public class myRealClass
{
public myRealClass(){}
public string myworkingMethod()
{
var retValue = String.Empty;
retValue = utilSvc.GetConfigurationValue();
return retValue;
}
}
public static class utilSvc
{
public static string GetConfigurationValue()
{
ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
}
}
the Test using Moq
[TestFixture(TestName = "Tests")]
public class Tests
{
private Mock<IConfigHelper> configHelperMOCK;
[SetUp]
public void Setup()
{
configHelperMOCK = new Mock<IConfigHelper>();
}
[Test]
public void serviceIsBPManagementForValidSource()
{
//Arrange
string sectionName = "sectionName/sectionElement";
string clinicalElementName = "ClinicalSystem";
string clinicalElementValue = "Zedmed";
configHelperMOCK.Setup(s => s.GetConfiguration(sectionName, clinicalElementName)).Returns(clinicalElementValue);
//act
// the call to myRealClass
//assert
// test assertions
}
}
The issue that I am having is with this line:
ConfigHelper configUtil = new ConfigHelper(); //NOT BEING MOCKED
I cannot get the moq to Mock the object.
I do not want the code to read the config file. I wish to moq away this instance of ConfigHelper
You can't wrap the static class/method but you can redirect it
public static class UtilSvc
{
static UtilSvc()
{
CreatorFunc = () => new ConfigHelper();
}
public static Func<IConfigHelper> CreatorFunc { get; set; }
public static string GetConfigurationValue()
{
var configUtil = CreatorFunc();
return configUtil.GetConfiguration("sectionName/sectionElement",
"ClinicalSystem");
}
}
and then in the test
//...
private Mock<IConfigHelper> configHelperMOCK;
[SetUp]
public void Setup()
{
configHelperMOCK = new Mock<IConfigHelper>();
UtilService.CreatorFunc = () => configHelperMOCK.Object;
}
//...
You cannot mock static class. I would rather propose to inject that IConfigHelper into the myRealClass. That is the usual way how to decouple dependencies and use DI.
public class myRealClass
{
private IConfigHelper _configHelper;
public myRealClass(IConfigHelper configHelper)
{
_configHelper = configHelper;
}
public string myworkingMethod()
{
var retValue = String.Empty;
retValue = _configHelper.GetConfigurationValue();
return retValue;
}
}
Avoid coupling your code to static classes, which in most cases cause you code be to difficult to maintain and test.
Follow the Explicit Dependencies Principle
Methods and classes should explicitly require (typically through
method parameters or constructor parameters) any collaborating objects
they need in order to function correctly.
Give the article a read. It is short and very informative.
If you want to keep the static class then you wrap the static class behind an abstraction.
public interface IUtilSvc {
string GetConfigurationValue();
}
public class utilSvcWrapper : IUtilSvc {
public string GetConfigurationValue() {
return utilSvc.GetConfigurationValue(); //Calling static service
}
}
Or another option is that utlSvc does not have to be static if can be injected into dependent classes
public class utilSvc : IUtilScv {
private readonly IConfigHelper configUtil;
public utilSvc(IConfigHelper configHelper) {
configUtil = configHelper;
}
public string GetConfigurationValue() {
return configUtil.GetConfiguration("sectionName/sectionElement", "ClinicalSystem");
}
}
Inject the IUtilScv into the dependent class so that it is no longer dependent on static class.
public class myRealClass {
private readonly IUtilScv utilSvc;
//Explicit dependency inject via constructor
public myRealClass(IUtilScv utilSvc) {
this.utilSvc = utilSvc;
}
public string myworkingMethod() {
var retValue = utilSvc.GetConfiguration();
return retValue;
}
}
In that case you don't even need IConfigHelper when testing as it has also been abstracted away. And you only need to mock the dependencies needed for the test.
[TestFixture(TestName = "Tests")]
public class Tests {
private Mock<IUtilScv> utilScvMOCK;
[SetUp]
public void Setup() {
utilScvMOCK = new Mock<IUtilScv>();
}
[Test]
public void serviceIsBPManagementForValidSource() {
//Arrange
var expectedClinicalElementValue = "Zedmed";
utilScvMOCK
.Setup(s => s.GetConfiguration())
.Returns(expectedClinicalElementValue)
.Verifiable();
var sut = new myRealClass(utilScvMOCK.Object);
//Act
var actualClinicalElementValue = sut.myworkingMethod();
//Assert
configHelperMOCK.Verify();
Assert.AreEqual(expectedClinicalElementValue, actualClinicalElementValue);
}
}
I was just working on some application architecture and this may sound like a stupid question but please explain to me how the following works:
Interface:
public interface IMatterDAL
{
IEnumerable<Matter> GetMattersByCode(string input);
IEnumerable<Matter> GetMattersBySearch(string input);
}
Class:
public class MatterDAL : IMatterDAL
{
private readonly Database _db;
public MatterDAL(Database db)
{
_db = db;
LoadAll(); //Private Method
}
public virtual IEnumerable<Matter> GetMattersBySearch(string input)
{
//CODE
return result;
}
public virtual IEnumerable<Matter> GetMattersByCode(string input)
{
//CODE
return results;
}
Controller:
public class MatterController : ApiController
{
private readonly IMatterDAL _publishedData;
public MatterController(IMatterDAL publishedData)
{
_publishedData = publishedData;
}
[ValidateInput(false)]
public JsonResult SearchByCode(string id)
{
var searchText = id; //better name for this
var results = _publishedData.GetMattersBySearch(searchText).Select(
matter =>
new
{
MatterCode = matter.Code,
MatterName = matter.Name,
matter.ClientCode,
matter.ClientName
});
return Json(results);
}
This works, when I call my controller method from jquery and step into it, the call to the _publishedData method, goes into the class MatterDAL.
I want to know how does my controller know to go to the MatterDAL implementation of the Interface IMatterDAL. What if I have another class called MatterDAL2 which is based on the interface. How will my controller know then to call the right method?
I am sorry if this is a stupid question, this is baffling me.
EDIT:
Based on the responses, it seems like this is where the dependency is being resolved:
This is a ninject call:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICpdMatterDAL>().To<CachedCpdData>();
}
Where CachedCpdData is:
public class CachedCpdData : ICpdMatterDAL
{
private static readonly object CacheLockObject = new object();
private readonly MatterDAL _matterData;
public CachedCpdData()
{
_matterData = DomainModel.DataAccessManager.Instance.Matters;
}
public IEnumerable<Matter> GetMattersForAutoCompleteByCode(string input)
{
var cacheKey = string.Format("matter-search-{0}", input ?? "");
var result = HttpRuntime.Cache[cacheKey] as IEnumerable<Matter>;
if (result == null)
{
lock (CacheLockObject)
{
result = HttpRuntime.Cache[cacheKey] as IEnumerable<Matter>;
if (result == null)
{
result = _matterData.GetMattersForAutoCompleteByCode(input).ToList();
HttpRuntime.Cache.Insert(cacheKey, result, null, DateTime.Now.AddSeconds(60), TimeSpan.Zero);
}
}
}
return result;
}
public IEnumerable<Matter> GetMattersByMatterCodeSearch(string input)
{
return _matterData.GetMattersByMatterCodeSearch(input);
}
}
The rason why your code is using the right implementation of IMatterDAL is because it's being passed as a parameter in the constructor of MatterController. I'm almost sure that your code is using some Dependency Injection framework to resolve IMatterDAL.
In fact Ninject is a DI Framework. Your code should have something like
kernel.Bind<IMatterDAL>().To<MatterDAL >();