System.ArgumentNullException is thrown from System.Web.HttpBrowserCapabilities constructor when mail.Send() from test called. It requires httpBrowserCapabilities parameter.
Code I have used:
EmailModel:
public class EmailModel :Email
{
public EmailModel() :base("EmailModel")
{
}
public string To = "**********#gmail.com";
[Display(ResourceType = typeof(Resource), Name = "Name")]
[Required(ErrorMessageResourceName = "Error_NameRequired", ErrorMessageResourceType = typeof(Resource))]
public string Name { get; set; }
[DataType(DataType.EmailAddress)]
[Display(ResourceType = typeof (Resource), Name = "Email")]
[Required(ErrorMessageResourceName = "Error_EmailRequired", ErrorMessageResourceType = typeof(Resource))]
[RegularExpression(".+#.+", ErrorMessageResourceName = "Error_EmailWrong", ErrorMessageResourceType = typeof(Resource))]
public string Email { get; set; }
[Display(ResourceType = typeof(Resource), Name = "Topic")]
[Required(ErrorMessageResourceName = "Error_TopicRequired", ErrorMessageResourceType = typeof(Resource))]
public string Topic { get; set; }
[Display(ResourceType = typeof(Resource), Name = "Massage")]
[DataType(DataType.MultilineText)]
[Required(ErrorMessageResourceName = "Error_MassageRequired", ErrorMessageResourceType = typeof(Resource))]
public string Massage { get; set; }
}
View of message:
#model *******.Models.EmailModel
#{
Layout = null;
}
To: #Model.To
Subject: #Model.Topic
The massage from #Model.Name #Model.Email
Massage text:
#Model.Massage
UnitTest :
public class EmailSetter
{
public const string DefaultName = "NameTest";
public const string DefaultEmail = "EmailTest#domaintest.test";
public const string DefaultTopic = "TopicTest";
public const string DefaultMassage = "Massage test.";
public EmailModel GetEmail(string Name = DefaultName, string Topic = DefaultTopic, string Email = DefaultEmail, string Massage = DefaultMassage)
{
EmailModel email = new EmailModel();
email.Name = Name;
email.Topic = Topic;
email.Email = Email;
email.Massage = Massage;
return email;
}
}
[TestClass]
public class MailTests
{
[TestMethod]
public void MailSendWithRightModel()
{
//Arrange
HomeController controller = new HomeController();
Mock<EmailModel> email = new Mock<EmailModel>();
email.Object.Name = EmailSetter.DefaultName;
email.Object.Email = EmailSetter.DefaultName;
email.Object.Topic = EmailSetter.DefaultTopic;
email.Object.Massage = EmailSetter.DefaultMassage;
//Act
controller.Contact(email.Object);
//Assert
email.Verify(mail => mail.Send());
}
}
Controller from test:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Contact(EmailModel emailModel)
{
if(ModelState.IsValid)
{
ViewBag.Send = true;
emailModel.Send();
ModelState.Clear();
}
return View(emailModel);
}
What I have tried (in unit test method):
creating new HttpBrowserCapabilities through new
BrowserCapabilitiesFactory
creating new
HttpBrowserCapabilitiesWrapper with HttpBrowserCapabilities
object
Any ideas how to make code not to throw exception? (i.e. make current HttpBrowserCapabilitiesWrapper get existing HttpBrowserCapabilities) :)
Source: Postal Unit Testing
When unit testing code that uses Postal, you may want to verify that
an email would be sent, without actual sending it.
Postal provides an interface IEmailService and an implementation,
EmailService, which actually sends email.
Assuming you use some kind of IoC container, configure it to inject an
IEmailService into your controller.
Then use the service to send email objects (instead of calling
Email.Send()).
public class HomeController : Controller {
readonly IEmailService emailService;
public HomeController(IEmailService emailService) {
this.emailService = emailService;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Contact(EmailModel email) {
if(ModelState.IsValid) {
emailService.Send(email);
ViewBag.Send = true;
ModelState.Clear();
}
return View(email);
}
}
Test this controller by creating a mock of the IEmailService
interface.
[TestClass]
public class MailTests {
[TestMethod]
public void MailSendWithRightModel() {
//Arrange
var mockService = new Mock<IEmailService>();
var controller = new HomeController(mockService.Object);
var email = new EmailSetter().GetEmail();
//Act
controller.Contact(email);
//Assert
mockService.Verify(m => m.Send(email));
}
}
Related
I have strict API routing requirements. This must be a Get request and the routing cannot be changed. The user can search for people by name, but he can also search for them by part of the name:
api/People?name=Tom - "Tom" can be in any part of the name, ignore case
api/People?name:contains=Tom - "Tom" must be at the beginning of the name, ignore case
api/People?name:exact=Tom - "Tom" must be case sensitive
How it should be implemented in the controller?
There is an option with 3 parameters, can this be improved?
public async Task<IActionResult> GetByName(
[FromQuery(Name = "name")] string name,
[FromQuery(Name = "name:contains")] string beginName,
[FromQuery(Name = "name:exact")] string exactName)
You can use custom model binding,here is a demo:
NameModel:
public class NameModel {
public string name { get; set; }
public string beginName { get; set; }
public string exactName { get; set; }
}
Controller:
[Route("/GetByName")]
public async Task<IActionResult> GetByName([ModelBinder(typeof(NameBinder))]NameModel nameModel)
{
return Ok();
}
NameBinder:
public class NameBinder:IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var model1 = new NameModel();
var name=bindingContext.ValueProvider.GetValue("name").FirstValue;
var beginName = bindingContext.ValueProvider.GetValue("name:contains").FirstValue;
var exactName = bindingContext.ValueProvider.GetValue("name:exact").FirstValue;
if (name.ToLower().Contains("tom")) {
model1.name = name;
}
if (beginName.ToLower().StartsWith("tom")) {
model1.beginName = beginName;
}
if (exactName.Contains("Tom")) {
model1.exactName = exactName;
}
bindingContext.Result = ModelBindingResult.Success(model1);
return Task.CompletedTask;
}
}
result:
I'm learning and practicing CQRS and Unit Test in C#. I need to know how to unit test Command Handlers in CQRS.
Here is my CreateAuthorCommand:
public sealed class CreateAuthorCommand : ICommand<Author>
{
public CreateAuthorCommand(string firstName, string lastName, DateTimeOffset dateOfBirth, string mainCategory, IEnumerable<CreateBookDto> books)
{
FirstName = firstName;
LastName = lastName;
DateOfBirth = dateOfBirth;
MainCategory = mainCategory;
Books = books;
}
public string FirstName { get; }
public string LastName { get; }
public DateTimeOffset DateOfBirth { get; private set; }
public string MainCategory { get; }
public IEnumerable<CreateBookDto> Books { get; }
[AuditLog]
[DatabaseRetry]
internal sealed class AddCommandHandler : ICommandHandler<CreateAuthorCommand, Author>
{
private readonly IUnitOfWork _unitOfWork;
public AddCommandHandler(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
}
public async Task<Result<Author>> Handle(CreateAuthorCommand command)
{
var nameResult = Name.Create(command.FirstName, command.LastName);
var birthDateResult = BirthDate.Create(command.DateOfBirth);
var mainCategoryResult = Entities.Authors.MainCategory.Create(command.MainCategory);
var authorResult = Result.Combine(nameResult, birthDateResult, mainCategoryResult)
.Map(() => new Author(nameResult.Value, birthDateResult.Value, null, mainCategoryResult.Value));
if (authorResult.IsFailure)
return Result.Failure<Author>(authorResult.Error);
await _unitOfWork.AuthorRepository.AddAsync(authorResult.Value);
await _unitOfWork.SaveChangesAsync();
return Result.Success(authorResult.Value);
}
}
}
Here is my Unit Test:
public class CreateAuthorCommandTests
{
[Fact]
public void Create_Author_Should_Call_Add_Method_Once()
{
var fixture = new Fixture();
var command = fixture.Create<CreateAuthorCommand>();
var mockUnitOfWork = new Mock<IUnitOfWork>();
var mockHandler = new Mock<ICommandHandler<CreateAuthorCommand, Author>>();
var result = mockHandler.Object.Handle(command);
mockUnitOfWork.Verify(x => x.AuthorRepository.AddAsync(It.IsAny<Author>()), Times.Once);
}
}
When I debug the above test,
I'm not able to step into Handler Method!!
How to pass mockUnitOfWork as constructor parameter to AddCommandHandler?
If I can pass mockUnitOfWork, the I can verify the AddAsync call inside my AuthorRepository. Please assist on what I'm doing wrong.
You are testing the handler, so instead of this
var result = mockHandler.Object.Handle(command);
...create an actual instance of AddCommandHandler and inject the dependencies it requires, i.e.
var mockUnitOfWork = new Mock<IUnitOfWork>();
var handler = new AddCommandHandler(mockUnitOfWork.Object);
When you call in to Handle, you'll now be stepping into your implementation.
If you want to keep the Internal access modifier, you can use InternalsVisibleTo attribute to grant access to your test project, e.g. do this in your project that defines the Handler,
[assembly: InternalsVisibleTo(“UnitTests”)]
https://anthonygiretti.com/2018/06/27/how-to-unit-test-internal-classes-in-net-core-applications/
I am trying to unit test a method within my controller in my Web API using XUnit. The role of the method is to get a single title, by ISBN, from the database. The issue I came across during unit testing is that I am unsure how to insert the dummy data that I must perform the test on, as well as how the Assert function works.
TitleController.cs
[ApiController]
[Route("titlecontroller")]
public class TitleController : Controller
{
private IGtlTitleRepository _gtlTitleRepository;
public TitleController(IGtlTitleRepository gtlTitleRepository)
{
_gtlTitleRepository = gtlTitleRepository;
}
[Route("getTitle/{ISBN}")]
[HttpGet()]
public GtlTitle GetTitle(string ISBN)
{
return _gtlTitleRepository.GetTitle(ISBN);
}
}
IGtlTitleRepository.cs
public interface IGtlTitleRepository
{
GtlTitle GetTitle(string ISBN);
}
MockGtlTitleRepository.cs
public class MockGtlTitleRepository : IGtlTitleRepository
{
private readonly string _connection;
public MockGtlTitleRepository(IOptions<ConnectionStringList> connectionStrings)
{
_connection = connectionStrings.Value.GTLDatabase;
}
private List<GtlTitle> _titleList;
public GtlTitle GetTitle(string ISBN)
{
using (var connection = new SqlConnection(_connection))
{
connection.Open();
return connection.QuerySingle<GtlTitle>("GetTitleByISBN", new { ISBN }, commandType: CommandType.StoredProcedure);
}
}
}
Right, as for my test code, I was able to write the following code, but as I said above, I can't figure out a proper way to test the method.
public class UnitTest1
{
[Fact]
public void Test1()
{
var repositoryMock = new Mock<IGtlTitleRepository>();
var title = new GtlTitle();
repositoryMock.Setup(r => r.GetTitle("978-0-10074-5")).Returns(title);
var controller = new TitleController(repositoryMock.Object);
var result = controller.GetTitle("978-0-10074-5");
// assert??
repositoryMock.VerifyAll();
}
}
What should be done within this unit test in order to properly test the method?
EDIT:
GtlTitle.cs
public class GtlTitle
{
public string ISBN { get; set; }
public string VolumeName { get; set; }
public string TitleDescription { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PublisherName { get; set; }
}
Before going to testing, there are a few things I recommend updating in your code:
Make your repository methods and controller actions async (thus web server can process requests while waiting for database roundtrips for previous calls)
Use ActionResult as an action return type. This way you can send different http status codes to the client.
Return 404 NotFound status code when title not found instead of returning successful result with null as payload.
Consider using a RESTful approach for API endpoints. E.g. base uri for titles resource should be something like api/titles
Don't specify getTitle for getting title endpoint, because you know HTTP verb which endpoint is mapped to (GET) and base resource url (api/titles).
With these notes applied:
[ApiController]
[Route("api/titles")]
public class TitleController : Controller
{
private IGtlTitleRepository _gtlTitleRepository;
public TitleController(IGtlTitleRepository gtlTitleRepository)
{
_gtlTitleRepository = gtlTitleRepository;
}
[HttpGet("{ISBN}")] // GET api/titles/{ISBN}
public async Task<ActionResult<GtlTitle>> GetTitle(string ISBN)
{
var title = await _gtlTitleRepository.GetTitle(ISBN);
if (title == null)
return NotFound();
return title;
}
}
Testing successful title retrieving:
[Fact]
public async Task Should_Return_Title_When_Title_Found()
{
var repositoryMock = new Mock<IGtlTitleRepository>();
var title = new GtlTitle();
repositoryMock.Setup(r => r.Get("978-0-10074-5")).Returns(Task.FromResult(title));
var controller = new TitleController(repositoryMock.Object);
var result = await controller.GetTitle("978-0-10074-5");
Assert.Equal(title, result.Value);
}
When title not found:
[Fact]
public async Task Should_Return_404_When_Title_Not_Found()
{
var repositoryMock = new Mock<IGtlTitleRepository>();
repositoryMock.Setup(r => r.Get("978-0-10074-5")).Returns(Task.FromResult<GtlTitle>(null));
var controller = new TitleController(repositoryMock.Object);
var result = await controller.GetTitle("978-0-10074-5");
Assert.IsType<NotFoundResult>(result.Result);
}
I have used custom InputFormatters for creating a subset of request from the generic request that request body receives in API request.
var reqModel = new XmlSerializer(CurrentType).Deserialize(xmlDoc.CreateReader());
SubSetRequest model = ConvertToSubSetRequestObject(reqModel as BigRequest);
return InputFormatterResult.Success(model);
Now in controller ModelState.IsValid is not pointing to SubSetRequest but to BigRequest, which I have received request body
public ActionResult<object> Calculate(SubSetRequest request)
{
if (!ModelState.IsValid){ }
// other codes..
}
Any idea how can we validate ModelState against SubSetRequest type.
Important Classes :
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore(options =>
{
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
options.InputFormatters.Insert(0, new XMLDocumentInputFormatter());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();
}
BigRequest.cs
[System.SerializableAttribute()]
public class BigRequest
{
public string Name { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public string Company { get; set; }
public string Designation { get; set; }
public string CompanyAddress { get; set; }
}
SubSetRequest.cs
[System.SerializableAttribute()]
public class SubSetRequest
{
public string Name { get; set; }
[Required] //This should tiger **Validation** error
public string Email { get; set; }
public string Address { get; set; }
}
XMLDocumentInputFormatter.cs
internal class XMLDocumentInputFormatter : InputFormatter
{
private Type CurrentType { get; set; }
public XMLDocumentInputFormatter()
{
SupportedMediaTypes.Add("application/xml");
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
using (var streamReader = new StreamReader(context.HttpContext.Request.Body))
{
CurrentType = typeof(BigRequest);
var xmlDoc = await XDocument.LoadAsync(streamReader, LoadOptions.None, CancellationToken.None);
var reqModel = new XmlSerializer(CurrentType).Deserialize(xmlDoc.CreateReader());
var model = ConvertToSubSetRequestObject(reqModel as BigRequest);
return InputFormatterResult.Success(model);
}
}
public SubSetRequest ConvertToSubSetRequestObject(BigRequest request)
{
var retObject = new SubSetRequest
{
Name = request.Name,
Address = request.Address
};
return retObject;
}
}
ValueController.cs
[HttpPost]
[Route("/api/Value/Calculate")]
public virtual ActionResult<object> Calculate(SubSetRequest request)
{
TryValidateModel(request);
if (ModelState.IsValid) // is giving as TRUE, even if EMAIL is NULL
{
var context = new ValidationContext(request, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
// this is working properly
var isValid = Validator.TryValidateObject(request, context, results);
}
return new ActionResult<object>(request.ToString());
}
When writing a unit test for an action that uses User.Identity, shows an error Unable to cast object of type 'Castle.Proxies.IIdentityProxy' to type 'MyApp.Web.Models.CurrentUser'.
Current user class :
[Serializable]
public class CurrentUser : IIdentity
{
public CurrentUser (){}
public CurrentUser (string name, string displayName, int userId)
{
this.Name = name;
this.DisplayName = displayName;
this.UserId = userId;
}
public CurrentUser (string name, string displayName, int userId,string roleName)
{
this.Name = name;
this.DisplayName = displayName;
this.UserId = userId;
this.RoleName = roleName;
}
public CurrentUser (string name, UserInfo userInfo)
: this(name, userInfo.DisplayName, userInfo.UserId,userInfo.RoleName)
{
if (userInfo == null) throw new ArgumentNullException("userInfo");
this.UserId = userInfo.UserId;
}
public CurrentUser (FormsAuthenticationTicket ticket)
: this(ticket.Name, UserInfo.FromString(ticket.UserData))
{
if (ticket == null) throw new ArgumentNullException("ticket");
}
public string Name { get; private set; }
public string AuthenticationType
{
get { return "GoalSetterForms"; }
}
public bool IsAuthenticated
{
get { return true; }
}
public string DisplayName { get; private set; }
public string RoleName { get; private set; }
public int UserId { get; private set; }
}
Controller Action
public PartialViewResult SomeList()
{
int userid = ((CurrentUser )(User.Identity)).UserId; //Error shows here
var list= listService.GetLists(userid);
return PartialView("ListView", list);
}
Unit Test
[Test]
public void SomeList_Test()
{
var controllerContext = new Mock<ControllerContext>();
var principal = new Moq.Mock<IPrincipal>();
ListController controller = new ListController (listService);
principal.SetupGet(x => x.Identity.Name).Returns("abc");
controllerContext.SetupGet(x => x.HttpContext.User).Returns(principal.Object);
controllerContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
controller.ControllerContext = controllerContext.Object;
}
Thanks in advance,
Razack
I just ran into this same thing but a little different.
replace
principal.SetupGet(x => x.Identity.Name).Returns("abc");
with
principal.SetupGet(x => x.Identity).Returns(new CurrentUser("abc", "test", 123));