Test class:
public class PlantDataControllerTests
{
private Mock<PlantDataService> serviceStub = new Mock<PlantDataService>();
private Mock<ILogger<PlantDataController>> loggerStub = new Mock<ILogger<PlantDataController>>();
private PlantDataController controller;
public PlantDataControllerTests()
{
serviceStub = new Mock<PlantDataService>(MockBehavior.Strict);
serviceStub.Setup(service => service.GetItemAsync(It.IsAny<string>()))
.ReturnsAsync((PlantData)null);
loggerStub = new Mock<ILogger<PlantDataController>>();
controller = new PlantDataController(serviceStub.Object, loggerStub.Object);
}
[Fact]
public async Task GetItemAsync_WithExisitingItem_ReturnsExpectedItem()
{
var expectedItem = CreateRandomPlantData();
serviceStub.Setup(service => service.GetItemAsync(It.IsAny<string>()))
.ReturnsAsync(expectedItem);
var result = await controller.GetItemAsync(expectedItem.Id);
result.Value.Should().BeEquivalentTo(expectedItem, opt => opt.ComparingByMembers<PlantData>());
}
[Fact]
public async Task GetItemAsync_WithExisitingItemByTopic_ReturnsExpectedItem()
{
//Arrange
var expectedItems = new[] { CreateRandomPlantData(), CreateRandomPlantData(), CreateRandomPlantData() };
serviceStub.Setup(s => s.GetItemsFromTopicAsync(It.IsAny<string>())).ReturnsAsync(expectedItems.ToList());
//Act
var actualItems = await controller.GetItemsFromTopicAsync("plant/soilHumidity/2");
//Assert
actualItems.Should().BeEquivalentTo(expectedItems, opt => opt.ComparingByMembers<PlantData>());
}
[Fact]
public async Task GetItemsAsync_WithExisitingItems_ReturnsAllItems()
{
var expectedItems = new[] {CreateRandomPlantData(), CreateRandomPlantData(), CreateRandomPlantData()};
serviceStub.Setup(s => s.GetItemsAsync()).ReturnsAsync(expectedItems.ToList());
var actualItems = await controller.GetItemsAsync();
actualItems.Should().BeEquivalentTo(expectedItems, opt => opt.ComparingByMembers<PlantData>());
}
private PlantData CreateRandomPlantData()
{
return new()
{
Id = "60ae78fd9afed8cbe0b30336",
TimeStamp = DateTime.Now,
Topic = "plant/soilHumidity/2",
Value = new Random().Next(0, 3000)
};
}
}
Controller:
[Route("api/[controller]")]
[EnableCors("MyPolicy")]
[ApiController]
public class PlantDataController : ControllerBase
{
private readonly PlantDataService plantDataService;
private readonly ILogger<PlantDataController> logger;
public PlantDataController(PlantDataService plantDataService, ILogger<PlantDataController> logger)
{
this.plantDataService = plantDataService;
this.logger = logger;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<PlantDataDto>>> GetItemsAsync()
{
var plantData = (await plantDataService.GetItemsAsync()).Select(p => p.AsDto());
var plantDataDtos = plantData.ToList();
logger.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}: Retrieved {plantDataDtos.ToList()}");
return Ok(plantDataDtos);
}
[HttpGet("{topic}", Name = "GetPlantDataFromTopic")]
public async Task<ActionResult<IEnumerable<PlantDataDto>>> GetItemsFromTopicAsync(string topic)
{
if(string.IsNullOrEmpty(topic))
return NotFound();
var plantData = (await plantDataService.GetItemsFromTopicAsync(HttpUtility.UrlDecode(topic))).Select(p => p.AsDto());
var plantDataDtos = plantData.ToList();
logger.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}: Retrieved {plantDataDtos.ToList()}");
return Ok(plantDataDtos);
}
[HttpGet("{id:length(24)}", Name = "GetPlantData")]
public async Task<ActionResult<PlantDataDto>> GetItemAsync(string id)
{
if(string.IsNullOrEmpty(id))
return NotFound();
var plantData = await plantDataService.GetItemAsync(id);
if (plantData is null)
{
return NotFound();
}
logger.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}: Retrieved plantdata: {plantData.Id}");
return Ok(plantData.AsDto());
}
}
Service:
public class PlantDataService : IPlantDataService
{
private readonly IMongoCollection<PlantData> _plants;
public PlantDataService(IPlantStarterDatabaseSettings settings)
{
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
_plants = database.GetCollection<PlantData>(settings.PlantStarterCollectionName);
}
public PlantDataService()
{
}
public virtual async Task<List<PlantData>> GetItemsAsync() => await _plants.Find(plant => true).ToListAsync();
public virtual async Task<PlantData> GetItemAsync(string id) => await _plants.Find(p => p.Id == id).SingleOrDefaultAsync();
public virtual async Task<List<PlantData>> GetItemsFromTopicAsync(string topic) => await _plants.FindAsync(p => p.Topic == topic).Result.ToListAsync();
}
AsDto method:
public static class DtoExtensions
{
public static PlantDataDto AsDto(this PlantData plantdata)
{
return new()
{
Id = plantdata.Id,
TimeStamp = plantdata.TimeStamp,
Topic = plantdata.Topic,
Value = plantdata.Value
};
}
}
Every assert result returns null.
I do not really know what goes wrong here...
The test all went green until some point i changed the Value from int to double, but that seems very strange that is the cause of my unit tests failing because the calls itself just works in swagger and Postman.
Related
i'm a problem with the SetAsyncValidator inside a RuleForeach to validate a collection. I need the async Validator because i need to check if the record already exist.
I use a Mediatr validation's pipeline in async mode.
Surfing the net I haven't found similars scenario. How can i realize that ?
Product Category class
public class ProductCategory : AuditableEntity, IAggregateRoot
{
public string Name { get; private set; }
private List<ProductSubCategory> _productSubCategories;
//public IEnumerable<ProductSubCategory> ProductSubCategories => _productSubCategories.AsReadOnly();
public List<ProductSubCategory> ProductSubCategories => _productSubCategories;
public ProductCategory()
{
_productSubCategories = new List<ProductSubCategory>();
}
public void AddProductSubcategory(string name)
{
var productSubCategory = new ProductSubCategory(name);
_productSubCategories.Add(productSubCategory);
}
}
public class ProductSubCategory : AuditableEntity, IAggregateRoot
{
public string Name { get; private set; }
public ProductSubCategory(string name)
{
Name = name;
}
public ProductSubCategory()
{
}
}
This is the synchronous rule and work very well but i would like to run asynchronously
When(x => x.ProductSubCategories.Any(), () =>
{
RuleForEach(x => x.ProductSubCategories).SetValidator(new CreateProductSubCategoryCommandValidator(_repositorySub));
});
Validation Pipeline: Handle method
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var typeName = request.GetGenericTypeName();
_logger.LogDebug("----- Validating command {CommandType}", typeName);
if (!_validators.Any())
return await next();
var context = new ValidationContext<TRequest>(request);
var validationResults = await Task.WhenAll(_validators
.Select(v => v.ValidateAsync(context, cancellationToken)));
var failures = validationResults.SelectMany(r => r.Errors)
.Where(f => f != null)
.ToList();
if (failures.Count == 0)
return await next();
_logger.LogDebug("----- Validating command {typeName} with failures {#failures}", typeName, failures );
throw new ValidationException(failures);
Thanks for any suggestion
PublishAsync not work
Example program.cs:
namespace MassTransitKafka
{
class Program
{
private static ServiceProvider _serviceProvider;
static async Task Main(string[] args)
{
var services = new ServiceCollection();
services.AddMassTransit(x =>
{
x.UsingInMemory((context, cfg) =>
{
cfg.ConfigureEndpoints(context);
});
x.AddRider(rider =>
{
rider.AddProducer<Enter1>(nameof(Enter1));
rider.AddProducer<Enter2>(nameof(Enter2));
rider.AddProducer<Enter3>(nameof(Enter3));
rider.AddProducer<EnterEnter>(nameof(EnterEnter));
rider.AddSagaStateMachine<TestSaga1StateMachine, TestSaga1State>(typeof(TestSaga1StateDefinition))
.InMemoryRepository();
rider.UsingKafka((context, k) =>
{
k.Host("localhost:9092");
k.TopicEndpoint<Null, Enter1>(nameof(Enter1), nameof(TestSaga1StateMachine), c =>
{
c.AutoOffsetReset = AutoOffsetReset.Earliest;
c.ConfigureSaga<TestSaga1State>(context);
});
k.TopicEndpoint<Null, Enter2>(nameof(Enter2), nameof(TestSaga1StateMachine), c =>
{
c.AutoOffsetReset = AutoOffsetReset.Earliest;
c.ConfigureSaga<TestSaga1State>(context);
});
k.TopicEndpoint<Null, Enter3>(nameof(Enter3), nameof(TestSaga1StateMachine), c =>
{
c.AutoOffsetReset = AutoOffsetReset.Earliest;
c.ConfigureSaga<TestSaga1State>(context);
});
k.TopicEndpoint<Null, EnterEnter>(nameof(EnterEnter), nameof(TestSaga1StateMachine), c =>
{
c.AutoOffsetReset = AutoOffsetReset.Earliest;
c.ConfigureSaga<TestSaga1State>(context);
});
});
});
});
_serviceProvider = services.BuildServiceProvider();
var busControl = _serviceProvider.GetRequiredService<IBusControl>();
var observer = new ReceiveObserver();
busControl.ConnectReceiveObserver(observer);
await busControl.StartAsync();
var tokenSource = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(s =>
{
Work(busControl, tokenSource.Token).GetAwaiter().GetResult();
});
while (true)
{
var quit = Console.ReadLine();
if (quit == "quit")
{
tokenSource.Cancel();
break;
}
}
}
private static async Task Work(IPublishEndpoint publisher, CancellationToken token)
{
var correlationId = Guid.NewGuid();
var enter1Producer = _serviceProvider.GetRequiredService<ITopicProducer<Enter1>>();
await enter1Producer.Produce(new {CorrelationId = correlationId, EnteredText = "1"}, token);
while (token.IsCancellationRequested == false)
{
var cancelled = token.WaitHandle.WaitOne(5000);
if (cancelled)
break;
}
}
private static Dictionary<string, string> Configuration
{
get
{
return new Dictionary<string, string>
{
{ "bootstrap.servers", "localhost:9092" },
{ "group.id", "saga.group.id" }
};
}
}
}
}
Example TestSaga1StateMachine.cs
public class TestSaga1StateMachine : MassTransitStateMachine<TestSaga1State>
{
public TestSaga1StateMachine()
{
InstanceState(_ => _.CurrentState);
Event(() => Enter1Event, x => x.CorrelateById(ctx => ctx.Message.CorrelationId));
Event(() => Enter2Event, x => x.CorrelateById(ctx => ctx.Message.CorrelationId));
Event(() => Enter3Event, x => x.CorrelateById(ctx => ctx.Message.CorrelationId));
Event(() => EnterEnterEvent, x => x.CorrelateById(ctx => ctx.Message.CorrelationId));
Initially(
When(Enter1Event)
.Then(context => context.Instance.SaveEnter1(context.Data))
// Messages are not sent here
.PublishAsync(context => context.Init<Enter2>(new {EnteredText = "2"}))
.TransitionTo(Entered1)
);
During(Entered1,
When(Enter2Event)
.Then(context => context.Instance.SaveEnter2(context.Data))
// Messages are not sent here
.PublishAsync(context => context.Init<Enter3>(new {EnteredText = "3"}))
.TransitionTo(Entered2)
);
During(Entered2,
When(Enter3Event)
.Then(context => context.Instance.SaveEnter3(context.Data))
// Messages are not sent here
.PublishAsync(context => context.Init<EnterEnter>(new {EnteredText = "Enter"}))
.TransitionTo(Entered3)
);
During(Entered3,
When(EnterEnterEvent)
.Then(context => context.Instance.Print())
.TransitionTo(EnteredEnter)
.Finalize());
SetCompletedWhenFinalized();
}
public State Entered1 { get; set; }
public State Entered2 { get; set; }
public State Entered3 { get; set; }
public State EnteredEnter { get; set; }
public Event<Enter1> Enter1Event { get; set; }
public Event<Enter2> Enter2Event { get; set; }
public Event<Enter3> Enter3Event { get; set; }
public Event<EnterEnter> EnterEnterEvent { get; set; }
}
This project is just for my learning.
I don't understand how to produce message up there
The bus configuration is identical to the one in the documentation. The first Enter1 message is published successfully and the saga receives it, but how to send a message to kafka from the saga is not clear
You would need to create a custom state machine activity, with a dependency on the producer interface (setup when Kafka is configured), in order to produce messages to Kafka topics. I recently did a video on this as part of Season 2.
You can see an example of producer setup in the unit tests
services.AddMassTransit(x =>
{
x.AddRider(rider =>
{
rider.AddProducer<KafkaMessage>(Topic);
rider.UsingKafka((context, k) =>
{
k.Host("localhost:9092");
});
});
});
Then, in your custom state machine activity, you'd add a constructor dependency on on ITopicProducer<KafkaMessage> and use that to produce the message. It may look similar to this one:
public class ProduceEnter2Activity :
Activity<TestSaga1State>
{
readonly ITopicProducer<Enter2> _producer;
public ProduceEnter2Activity(ITopicProducer<Enter2> producer)
{
_producer = producer;
}
public void Probe(ProbeContext context)
{
context.CreateScope("notifyMember");
}
public void Accept(StateMachineVisitor visitor)
{
visitor.Visit(this);
}
public async Task Execute(BehaviorContext<TestSaga1State> context, Behavior<TestSaga1State> next)
{
await Execute(context);
await next.Execute(context);
}
public async Task Execute<T>(BehaviorContext<TestSaga1State, T> context, Behavior<TestSaga1State, T> next)
{
await Execute(context);
await next.Execute(context);
}
public Task Faulted<TException>(BehaviorExceptionContext<TestSaga1State, TException> context, Behavior<TestSaga1State> next)
where TException : Exception
{
return next.Faulted(context);
}
public Task Faulted<T, TException>(BehaviorExceptionContext<TestSaga1State, T, TException> context, Behavior<TestSaga1State, T> next)
where TException : Exception
{
return next.Faulted(context);
}
async Task Execute(BehaviorContext<TestSaga1State> context)
{
await _producer.Produce(new Enter2(...));
}
}
Then, in your state machine, you would use:
.Activity(x => x.OfInstanceType<ProduceEnter2Activity>())
I have a model called notes that I am feeding into a kendo grid via calls to an interface / repository class. Everything works but it is running synchronously and I want to run it asynchronously.
I'm using .NET core 3.1 so IAsyncEnumerable etc should all be available if I can work out how to do it. I've tried a lot of variations but always get errors. Any help much appreciated.
This is the interface
namespace FliveRetry.Models.PTs
{
public interface IPtNoteRepository
{
IEnumerable<Note> GetAllNotes();
Note GetNoteById(int NoteId);
}
}
This is the repository
namespace FliveRetry.Models.PTs
{
public class PtNoteRepository : IPtNoteRepository
{
private readonly FliveRetryContext context;
public PtNoteRepository(FliveRetryContext context)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
}
public IEnumerable<Note> GetAllNotes()
{
return context.Note;
}
public Note GetNoteById(int itemId)
{
var note = context.Note.SingleOrDefault(i => i.ID == itemId);
return note;
}
}
}
and this is the index model where I'm calling it and feeding it to the grid via OnPostRead
namespace FliveRetry.Pages.Notes
{
public class IndexModel : NoteSelectPageModel
{
private const int CURRENT_USER_ID = 21; //Fake user id for demo
private readonly IPtNoteRepository rpsNotesRepo;
public static IList<Note> Notes { get; set; }
[BindProperty(SupportsGet = true)]
public NoteScreenEnum? PresetScreen { get; set; }
public IndexModel(IPtNoteRepository rpsNotesData)
{
rpsNotesRepo = rpsNotesData;
}
public void OnGet()
{
IEnumerable<Note> notes;
switch (PresetScreen)
{
case NoteScreenEnum.GeneralNotes:
notes = rpsNotesRepo.GetAllNotes();
break;
case NoteScreenEnum.ThisNote:
notes = rpsNotesRepo.GetNoteByID(CURRENT_USER_ID);
break;
default:
notes = rpsNotesRepo.GetAllNotes();
break;
}
Notes = notes.ToList();
}
public JsonResult OnPostRead([DataSourceRequest] DataSourceRequest request)
{
return new JsonResult(Notes.ToDataSourceResult(request));
}
}
}
In other pages like create or edit.cshtml.cs for example I am successfully using async to edit and create, e.g:
namespace FliveRetry.Pages.Notes
{
public class EditModel : NoteSelectPageModel
{
private readonly FliveRetry.Data.FliveRetryContext _context;
public EditModel(FliveRetry.Data.FliveRetryContext context)
{
_context = context;
}
[BindProperty]
public Note Note { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Note = await _context.Note
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Note == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(IFormCollection form, int? id, string[] selectedOrgs, string[] selectedClients, string[] selectedStaffs, string[] selectedNoteTypes)
{
if (!ModelState.IsValid)
{
return Page();
}
var noteToUpdate = await _context.Note
.FirstOrDefaultAsync(s => s.ID == id);
if (await TryUpdateModelAsync<Note>(noteToUpdate, "note", // Prefix for form value.
c => c.Title, c => c.NoteText, c => c.NoteDate, c => c.Amount, c => c.ImageURL, c => c.FileURL, c => c.Archived, c => c.DateSaved, c => c.UserID, c => c.StartTime, c => c.FinishTime))
{
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
}
}
Try to use below code to convert synchronous action to asynchronous action:
IPtNoteRepository:
public interface IPtNoteRepository
{
Task<IEnumerable<Note>> GetAllNotesAsync();
Task<Note> GetNoteByIdAsync(int NoteId);
}
Repository:
public class PtNoteRepository : IPtNoteRepository
{
private readonly FliveRetryContext context;
public PtNoteRepository(FliveRetryContext context)
{
this.context = context ?? throw new ArgumentNullException(nameof(context));
}
public async Task<IEnumerable<Note>> GetAllNotesAsync()
{
return await context.Note.ToListAsync();
}
public async Task<Note> GetNoteByIdAsync(int itemId)
{
var note = await context.Note.SingleOrDefaultAsync(i => i.ID == itemId);
return note;
}
}
IndexModel:
public async Task OnGetAsync()
{
IEnumerable<Note> notes;
switch (PresetScreen)
{
case NoteScreenEnum.GeneralNotes:
notes = await rpsNotesRepo.GetAllNotesAsync();
break;
case NoteScreenEnum.ThisNote:
notes = await rpsNotesRepo.GetNoteByIdAsync(CURRENT_USER_ID);
break;
default:
notes = await rpsNotesRepo.GetAllNotesAsync();
break;
}
Notes = notes.ToList();
}
Using Unit, I'm trying to setup a test where I can delete an entry from my Mock list in order to test out the implementation in my CustomerManager.
My Repository:
public class Repository<T> : IRepository<T> where T : ModelBase
{
private readonly CustomerDbContext _context;
public Repository(CustomerDbContext context)
{
if (_context == null)
throw new ArgumentNullException(nameof(context));
_context = context;
}
public async Task Delete(int id, bool softDelete = true)
{
var entity = await GetById(id);
if (entity == null) return;
if (softDelete)
{
entity.IsDeleted = true;
Save(entity);
}
else
_context.Set<T>().Remove(entity);
}
public async Task<T> GetById(int id)
{
return await _context.Set<T>().FirstOrDefaultAsync(t => t.Id == id).ConfigureAwait(false);
}
public void Save(T entity)
{
if (entity.Id == 0)
_context.Set<T>().Add(entity);
else
_context.Entry(entity).State = EntityState.Modified;
}
}
My Unit Of Work is:
public class CustomerUnitOfWork : IUnitOfWork
{
public CustomerDbContext Context { get; }
public async Task<int> Commit()
{
foreach(var item in Context.ChangeTracker.Entries().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified))
{
if (!(item.Entity is ModelBase entity))
continue;
if (item.State == EntityState.Added)
{
entity.CreatedDate = DateTime.UtcNow;
}
entity.ModifiedDate = DateTime.UtcNow;
}
return await Context.SaveChangesAsync();
}
}
My Manager is:
public class CustomerManager : ICustomerManager
{
private readonly IRepository<Models.Customer> _customerRepository;
protected readonly IUnitOfWork _unitOfWork;
public CustomerManager(IRepository<Models.Customer> customerRepository, IUnitOfWork unitOfWork)
{
_customerRepository = customerRepository;
_unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
}
public async Task<int> Delete(int id, bool softDelete = true)
{
await _customerRepository.Delete(id, softDelete);
return await _unitOfWork.Commit();
}
public async Task<Models.Customer> GetById(int id)
{
return await _customerRepository.GetById(id);
}
}
In my Unit Test, I'm setting up the constructor like this:
private ICustomerManager MockManager;
private Mock<IUnitOfWork> UnitOfWork;
private List<Models.Customer> Customers;
Mock<IRepository<Models.Customer>> MockRepository;
public CustomerTest()
{
MockRepository = new Mock<IRepository<Models.Customer>>();
UnitOfWork = new Mock<IUnitOfWork>();
Customers = new List<Models.Customer>()
{
new Models.Customer
{
Id = 1,
Name = "Foo",
City = "Baltimore",
Company = "Foo Company"
},
new Models.Customer
{
Id = 2,
Name = "Bar",
City = "Owings Mills",
Company = "Bar Company"
}
};
MockRepository.Setup(repo => repo.GetAll()).ReturnsAsync(Customers);
MockRepository.Setup(repo => repo.GetById(It.IsAny<int>())).ReturnsAsync((int i) => Customers.SingleOrDefault(c => c.Id == i));
MockRepository.SetupAllProperties();
MockManager = new CustomerManager(MockRepository.Object, UnitOfWork.Object);
}
In my test method, I want to remove the first object in my test list.
public async Task ShouldDelete()
{
var countBeforeDelete = Customers.Count();
var countAfterDelete = await MockManager.Delete(1, true);
Assert.Equal(countBeforeDelete, countAfterDelete);
}
However, CountAfterDelete always returns 0. I'm not sure what I'm doing wrong.
Your Manager's Delete method returns await _unitOfWork.Commit();
By default, the Mock of IUnitOfWork will return default(int) which is 0.
I have a project. It is simple License Manager.
I have such DbContext (Entity Framework 5):
public class EFDbContext : DbContext
{
public virtual IDbSet<License> Licenses { get; set; }
public virtual IDbSet<Department> Departments { get; set; }
public virtual IDbSet<Its> Itses { get; set; }
}
I created an IUnitOfWork implementation:
public class UnitOfWork : IUnitOfWork
{
private readonly EFDbContext Db;
private readonly LicenseRepository LicenseRepository;
private readonly DepartmentRepository DepartmentRepository;
private readonly ItsRepository ItsRepository;
public UnitOfWork(EFDbContext context)
{
Db = context;
DepartmentRepository = DepartmentRepository ?? (DepartmentRepository = new DepartmentRepository(Db));
ItsRepository = ItsRepository ?? (ItsRepository = new ItsRepository(Db));
LicenseRepository = LicenseRepository ?? (LicenseRepository = new LicenseRepository(Db));
}
public IRepository<License> Licenses
{
get { return LicenseRepository; }
}
public IRepository<Department> Departments
{
get { return DepartmentRepository; }
}
public IRepository<Its> Itses
{
get { return ItsRepository; }
}
}
And I have an IRepository implementation named LicenseRepository:
public class LicenseRepository : IRepository<License>
{
private EFDbContext Db;
public LicenseRepository(EFDbContext context)
{
Db = context;
}
public IQueryable<License> Items
{
get { return Db.Licenses.Include(lic => lic.Department).Include(lic => lic.Its); }
}
private bool CheckIfExist(string codeAct, int id)
{
return (id == 0)
? Db.Licenses.Any(license => license.CodeAct.Equals(codeAct))
: Db.Licenses.Any(license => license.CodeAct.Equals(codeAct) && license.Id != id);
}
private void Defend(License item)
{
Security sec = Security.Instance;
Its its = Db.Itses.FirstOrDefault(i => i.Id == item.ItsId);
item.CodeLic = item.CodeLic.ToUpper()/*.Replace("-", " - ")*/;
string hash = sec.GetHash(item.CodeLic.Replace(" - ", ""), (its == null) ? string.Empty : its.Salt);
item.CodeAct = hash.Insert(5," - ");
}
public IQueryable<License> GetOrderBy<TResult>(string order, IQueryable<License> items, Expression<Func<License, TResult>> key)
{
return (order == null || order.ToUpper() == "ASC") ? items.OrderBy(key) : items.OrderByDescending(key);
}
public void Edit(License item)
{
Defend(item);
if (CheckIfExist(item.CodeAct, item.Id)) throw new DbUpdateException("Error update");
if (item.Id == 0)
{
Db.Licenses.Add(item);
}
else
{
License dbEntry = Db.Licenses.Find(item.Id);
if (dbEntry != null)
{
dbEntry.CodeLic = item.CodeLic;
dbEntry.CodeAct = item.CodeAct;
dbEntry.Peom = item.Peom;
dbEntry.Phone = item.Phone;
dbEntry.Pib = item.Pib;
dbEntry.DepartmentId = item.DepartmentId;
dbEntry.ItsId = item.ItsId;
dbEntry.Dtime = item.Dtime;
}
}
Db.SaveChanges();
}
public License Delete(int id)
{
License dbEntry = Db.Licenses.Find(id);
if (dbEntry != null)
{
Db.Licenses.Remove(dbEntry);
Db.SaveChanges();
}
return dbEntry;
}
}
My LicenseController looks like:
public class LicenseController: Controller
{
private IUnitOfWork Repository;
public LicenseController(IUnitOfWork _repository)
{
Repository = _repository;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(LicenseViewModel model)
{
if (ModelState.IsValid) // валидация данных
{
model.License.DepartmentId = model.License.DepartmentId == 0 ? null : model.License.DepartmentId;
model.License.ItsId = model.License.ItsId == 0 ? null : model.License.ItsId;
if (model.License.Dtime == DateTime.MinValue)
{
model.License.Dtime = DateTime.Now;
}
model.License.CodeLic = string.Join(" - ", model.CodePart1, model.CodePart2, model.CodePart3,
model.CodePart4);
Repository.Licenses.Edit(model.License);
// хранится, пока мы их не прочитаем (ViewBag(не может хранить дольше текущего запроса) и Session(требует явного удаления) нам не подходят)
TempData["message"] = string.Format("Об'єкт \"{0}\" збережений", model.License.CodeLic);
return RedirectToAction("List");
}
// в случае неудачи
model.DropDownDepartment = GetDepartments(model.License.DepartmentId);
model.DropDownIts = GetItses(model.License.ItsId);
return View(model);
}
// another methods ...
}
I want to test if Edit method of Repository calls correctly when I pass correct Entity to it. My Mock class looks like:
private IDbSet<T> GetQueryableMockDbSet<T>(IQueryable<T> sourceList) where T: class
{
Mock<IDbSet<T>> mockSet = new Mock<IDbSet<T>>();
mockSet.As<IQueryable<T>>().Setup(t => t.Provider).Returns(sourceList.Provider);
mockSet.As<IQueryable<T>>().Setup(t => t.Expression).Returns(sourceList.Expression);
mockSet.As<IQueryable<T>>().Setup(t => t.ElementType).Returns(sourceList.ElementType);
mockSet.As<IQueryable<T>>().Setup(t => t.GetEnumerator()).Returns(sourceList.GetEnumerator());
return mockSet.Object;
}
[TestMethod]
public void CanSaveValidLicense()
{
// ARRANGE
// создали пустое имитированное хранилище
var mockContext = new Mock<EFDbContext>();
IUnitOfWork iuow = GetFakeRepository();
mockContext.Setup(context => context.Itses).Returns(GetQueryableMockDbSet(iuow.Itses.Items));
mockContext.Setup(context => context.Departments).Returns(GetQueryableMockDbSet(iuow.Departments.Items));
mockContext.Setup(context => context.Licenses).Returns(GetQueryableMockDbSet(iuow.Licenses.Items));
var mockRepo = new Mock<UnitOfWork>(mockContext.Object);
mockRepo.CallBase = true;
// создаем контроллер
LicenseController target = new LicenseController(mockRepo.Object);
// создаем объект типа License
LicenseViewModel lvm = new LicenseViewModel
{
License = new License
{
CodeAct = "ABCD",
CodeLic = "DCBA",
Peom = "111",
Phone = "777",
Pib = "BDV",
DepartmentId = 1,
ItsId = 1
}
};
// ACT
// пытаемся сохранить новосозданный объект
ActionResult result = target.Edit(lvm);
// ASSERT
// проверяем, что был вызван метод хранилища на сохранение
//mockContext.Verify(context => context.SaveChanges());
//licRep.Verify(repository => repository.Edit(lvm.License), Times.Once());
mockRepo.As<IRepository<License>>().Verify(work => work.Edit(lvm.License), Times.Once());
mockRepo.VerifyAll();
// проверяем, что сохранение прошло успешно - произошло перенаправление на страницу (не остались на текущей)
Assert.IsNotInstanceOfType(result, typeof (ViewResult));
}
I have a headache with Verify method. It throws NotSupportedException or if I try another way it throws another Exception. I'm new in unit-testing. So, can you please help me with this problem.