So we have a 3 tier Web API project which I am attempting to implement some tests for.
We are using Autofac as our IoC container, and its integration library with Moq to implement some unit testing.
Start with some code:
[TestMethod]
public void Get()
{
using (var mock = AutoMock.GetLoose())
{
// DATA LAYER
// mock some contact objects up
var contactsList = new List<CONTACT>
{
new CONTACT { CONTACT_ID = 1, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock1", SURNAME = "Mock1" },
new CONTACT { CONTACT_ID = 2, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock2", SURNAME = "Mock2" },
new CONTACT { CONTACT_ID = 3, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock3", SURNAME = "Mock3" },
new CONTACT { CONTACT_ID = 4, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock4", SURNAME = "Mock4" },
new CONTACT { CONTACT_ID = 5, CREATE_DTIME = DateTime.Now, UPDATE_DTIME = DateTime.Now, CONTACT_TYPE = "Mocked", FORENAME = "Mock5", SURNAME = "Mock5" },
}.AsQueryable();
var contactsSet = new Mock<DbSet<CONTACT>>();
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.Provider).Returns(contactsList.Provider);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.Expression).Returns(contactsList.Expression);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.ElementType).Returns(contactsList.ElementType);
contactsSet.As<IQueryable<CONTACT>>().Setup(m => m.GetEnumerator()).Returns(contactsList.GetEnumerator());
// mock that context
var mockedContext = new Mock<ExampleEntities>();
// set up context so that the contactsSet above is returned when queried.
mockedContext.Setup(c => c.CONTACTS).Returns(contactsSet.Object);
mock.Provide<IContactsRepository, ContactsRepository>(new TypedParameter(typeof(KCMEntities), mockedContext.Object));
mock.Provide<IContacts, Contacts>();
// Instantiate the controller we are performing the test on
var testController = mock.Create<ContactsController>();
// act
ContactListResult contactListResult = testController.Get();
// assert
Assert.AreEqual(true, contactListResult.Success);
Assert.AreEqual(5, contactListResult.ContactList.Count());
Assert.AreEqual("Mock1", contactListResult.ContactList.First().Forename);
Assert.AreEqual(1001, contactListResult.ContactList.First().Id); // The businesslogic layer has been doctored to add 1000 to the id...just to test it is passing through this layer..
mock.Mock<IContactsRepository>().Verify(x => x.GetContacts(), Times.Once());
}
}
We're not sure if this is really meant to be done but we are trying to write the test to test/mock the action of calling a controller down through all the layers of the application.
The IContactsRepository and the IContacts are the services sitting in the data layer and a business logic layer respectively.
We need to mock the dbcontext at the data layer to return our mocked data, and make sure autofac knows what components to provide for both services when resolving:
mock.Provide<IContactsRepository, ContactsRepository>(new TypedParameter(typeof(KCMEntities), mockedContext.Object));
mock.Provide<IContacts, Contacts>();
We're setting up the expectation that the mocked context is to be passed into the data layer when it is resolved. This is an overridden constructor on the concrete implementation of the IContactsRepository (tried a non overidden same issues). The default constructor simply news up a new dbcontext, no params.
This seems to work up to a point.
All asserts are fine until we try a verify:
mock.Mock<IContactsRepository>().Verify(x => x.GetContacts(), Times.Once());
This throws an error:
"Unable to cast object of type 'Data.Repositories.ContactsRepository' to type 'Moq.IMocked``1[Data.Interfaces.IContactsRepository]'."
Now for obvious reasons we want to use verify to verify certain methods are called on the components/services we have.
We've tried many variations of setup e.g. mock.Mock>IContacts>().Setup(.... etc etc, and not managed to work it out/set it up properly.
Essentially we are either doing something basically flawed in trying to test like this, or hopefully we are just doing incorrect setup.
Any suggestions on how to achieve this?
Thanks in advance for your help.
Related
I've been working on adding integration tests with XUnit and SQL Server. I'm facing a challenge: Have a clean database state before and after each test.
This is an example of my test:
[Fact]
public async Task GetAsync_ReturnsTaskOfRoleModelList()
{
using var context = _factory.CreateContext();
_rolesRepository = () => _factory.CreateRoleRepository(context);
await context.Database.BeginTransactionAsync();
await _rolesRepository().CreateAsync(new RoleModel
{
Id = Guid.NewGuid(),
Name = "Role A",
Description = "Role A Description",
CreatedDate = DateTime.Now,
UpdatedDate = DateTime.Now,
Deleted = false,
});
var response = await _client.GetFromJsonAsync<List<RoleModel>>("/api/roles");
response.ShouldNotBeNull();
response.ShouldBeOfType<List<RoleModel>>();
response.Count.ShouldBeGreaterThan(0);
context.ChangeTracker.Clear();
}
First a context is created, and then a repository is also created with that context. Then,
we start the transaction, add the data to the database, send the request and check the response, to finally clear the transaction. Unfortunately this test is failing.
Using this microsoft article as reference: https://learn.microsoft.com/en-us/ef/core/testing/testing-with-the-database.
This line does not create a new Role in the database:
await _rolesRepository().CreateAsync(new RoleModel
{
Id = Guid.NewGuid(),
Name = "Role A",
Description = "Role A Description",
CreatedDate = DateTime.Now,
UpdatedDate = DateTime.Now,
Deleted = false,
});
If I remove the lines related to the database transaction, the test passes and the role is added.
How can I solve this?
I'm trying to get the billing address from Stripe Checkout from a Webhook call.
What I'm trying to achieve is to get the information from the form in the yellow rectangle.
This is my Checkout configuration :
var options = new SessionCreateOptions()
{
CustomerEmail = user.Email,
BillingAddressCollection = "required",
ShippingAddressCollection = new SessionShippingAddressCollectionOptions
{
AllowedCountries = new List<string>
{
"FR",
},
},
PaymentMethodTypes = new List<string>() {
result.Payment.Type
},
LineItems = new List<SessionLineItemOptions>{
new SessionLineItemOptions
{
PriceData = new SessionLineItemPriceDataOptions
{
UnitAmountDecimal = result.Payment.Amount * 100,
Currency = result.Payment.Currency,
ProductData = new SessionLineItemPriceDataProductDataOptions
{
Name = _stringLocalizer.GetString("StripeProductLabel"),
},
},
Quantity = 1,
},
},
Mode = result.Payment.Mode,
SuccessUrl = $"{request.Scheme}://{request.Host}" + "/payment/complete",
CancelUrl = $"{request.Scheme}://{request.Host}" + "/payment/cancel",
Metadata = new Dictionary<string, string>()
{
{ Constants.StripeMetaDataOrderId, result.Id }
}
};
and when I receive the session objet in the completed event : session = stripeEvent.Data.Object as Stripe.Checkout.Session;
I can't get the information because the paymentIntent object is null ( information from : Retrieve Billing Address from Stripe checkout session? ).
This is an important feature from Sripe because the application is a B2B application to help professionals to create orders for their B2C business. It will avoid making custom code from something that exits in Stripe API :)
Thanks in advance
The answer you linked to is the correct way to get this information, from the payment_method on the payment_intent. I'm not sure how/why your payment_intent value would not be populated, as my testing indicates this to be initialized upon creating the session, even if I never redirect to it.
Are you certain you're creating a mode=payment session? I see that in the code you shared, but things will change a bit if you're actually doing setup or subscription mode.
I am making a ASP.NET core web api and I have used Entity Framework Core. I am trying to unit test the controllers using N-Unit
I am mocking the mapper and the repository. The APIs work fine and I have even created the front end or the API but I am having issues with the unit testing.
Controller Code-
[AllowAnonymous]
[HttpGet("{id}")]
public async Task<IActionResult> GetMovie(int id)
{
var movie = await _repo.GetMovie(id);
if (movie == null)
{
return BadRequest("Object with Id not found");
}
var movieToReturn = _mapper.Map<MovieForDetailedDto>(movie);
return Ok(movieToReturn);
}
[AllowAnonymous]
[HttpPost()]
public async Task<IActionResult> AddMovie(MovieForDetailedDto movieForDetailedDto)
{
if (await _repo.MovieExists(movieForDetailedDto.ATitle))
return BadRequest("movie already exists");
else if(!ModelState.IsValid || movieForDetailedDto.ATitle == null || movieForDetailedDto.APrice == null || movieForDetailedDto.AMovieDescription ==null)
{
return BadRequest("movie details not valid");
}
var movieToCreate = _mapper.Map<TblMovie>(movieForDetailedDto);
var createdMovie = await _repo.AddMovie(movieToCreate);
return Ok(createdMovie);
}
In all my functions, in the line where I map the DTO to the Model, the Line returns a null object during unit testing but the they work fine outside of unit testing.
My Unit Test Code for the controller-
[TestFixture]
public class MoviesControllerTests
{
private Mock<IMovieRepository> _mockMovieRepository;
private Mock<IMapper> _mockMovieMapper;
private MoviesController _moviesController;
[Test]
public async Task CallGetRequest_WhenCalledWithId_ReturnsTheMovieWithTheSameId()
{
getMoviesHelper getMoviesHelper = new getMoviesHelper();
List<TblMovie> movieList = getMoviesHelper.getMovieFromList();
var movie = getMoviesHelper.movieById(3);
_mockMovieRepository = new Mock<IMovieRepository>();
_mockMovieMapper = new Mock<IMapper>();
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>()))
.Returns(getMoviesHelper.movieById(3));
_mockMovieRepository.Setup(repo => repo.GetMovie(3))
.ReturnsAsync(getMoviesHelper.movieById(3));
_moviesController = new MoviesController(_mockMovieRepository.Object, _mockMovieMapper.Object);
var tblMovie = await _moviesController.GetMovie(3);
var okResult = tblMovie as OkObjectResult;
//Assert.AreEqual(200, okResult.StatusCode);
Assert.NotNull(tblMovie);
Assert.IsAssignableFrom<OkObjectResult>(tblMovie);
var result = ((OkObjectResult)tblMovie).Value;
var resultValue = ((OkObjectResult)tblMovie).Value as TblMovie;
Assert.AreEqual(resultValue.ATitle,"Raging Bull");
Assert.NotNull(result);
Assert.IsAssignableFrom<TblOrder>(result);
}
[Test]
public async Task GivenAValidMovie_WhenIPostANewMovie_ThenItReturnsOkWithResponse()
{
_mockMovieRepository = new Mock<IMovieRepository>();
_mockMovieMapper = new Mock<IMapper>();
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>()))
.Returns(new TblMovie());
_mockMovieRepository.Setup(repo => repo.AddMovie(It.IsAny<TblMovie>()))
.ReturnsAsync((TblMovie movie) => movie);
_moviesController = new MoviesController(_mockMovieRepository.Object, _mockMovieMapper.Object);
var tblMovie = await _moviesController.AddMovie(new MovieForDetailedDto
{
AMovieId = 55,
ATitle = "redemption",
AMovieDescription = "An action comedy adventure about brilliant robotics prodigy Hiro Hamada, who finds himself in the grips of a criminal plot that threatens to destroy the fast-paced, high-tech city of San Fransokyo. With the help of his closest companion-a robot named Baymax-Hiro joins forces with a reluctant team of first-time crime fighters on a mission to save their city.",
ADuration = "105 min",
APrice = "10",
APurchasePrice = "25",
ARating = 5,
AImageLink = "http://upload.wikimedia.org/wikipedia/en/4/4b/Big_Hero_6_%28film%29_poster.jpg",
ATrailerLink = "//www.youtube.com/embed/z3biFxZIJOQ",
AGenre = "Comedy",
AWideImage = "https://github.com/tushar23091998/MovieRentalApp-FrontEnd/blob/master/src/app/images/bighero6.jpg?raw=true"
});
var okResult = tblMovie as OkObjectResult;
Assert.AreEqual(200, okResult.StatusCode);
Assert.NotNull(okResult);
Assert.IsAssignableFrom<OkObjectResult>(tblMovie);
var result = ((OkObjectResult)tblMovie).Value;
Assert.NotNull(result);
Assert.IsAssignableFrom<TblMovie>(result);
}
In both the test cases and even in other test cases, the repo setup works fine but after defining the controller and when I call the controller function to get the value, I found in debugging that the mapper line in controller code returns null.
I am not sure how go about setting up the mockmapper now and how should I pass the value.
Debugging Output -
What we were doing in unit tests, when using AutoMapper, is instead of mocking it, rather initialize it, as descried here: https://kenbonny.net/2018/01/15/injecting-automapper-profiles-in-tests/
The cons I see doing this, is that your tests might be failing because of mapping issues and are not strictly testing the method.
Given your example, your setup is incorrect.
For the get method test, you should setup to return a MovieForDetailedDto for a TblMovie, but you are doing the opposite:
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>())).Returns(getMoviesHelper.movieById(3));
should be something like:
var expectedMovieDto = new MovieForDetailedDto(){//insert values here}
_mockMovieMapper.Setup(mapper => mapper.Map<MovieForDetailedDto>(It.IsAny<TblMovie>())).Returns(expectedMovieDto);
For the POST method, you are setting it up to return a new TblMovie (so all properties are set to their default values)
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>())) .Returns(new TblMovie());
should be:
var expectedMovie = new TblMovie()
{
ADuration = "some value",
AGenre = "some value"
//other }
_mockMovieMapper.Setup(mapper => mapper.Map<TblMovie>(It.IsAny<MovieForDetailedDto>())).Returns(expectedMovie);
I have an inheritance hierarchy where Owners, Tenants, Vendors and Agents are derived from People. In my WCF Data Service client, I want to use AddObject to create a new Owner, but I cannot find how to do this. When I try:
var owner = new Owner()
{
FirstName = "Test"
,LastName = "Person"
,CheckName = "Test Person"
,PersonNo = "Test"
,UseFullNameForName = false
,TypeOfPerson = "Owner"
};
//Add
context.AddObject("People", owner);
context.SaveChanges();
the service throws a dynamic sql error. I am using WCF Services 5.4 with EF 4.5.
you should create the entity type of the table.
var person = new Person {
FirstName = "Test"
,LastName = "Person"
,CheckName = "Test Person"
,PersonNo = "Test"
,UseFullNameForName = false
,TypeOfPerson = "Owner"
};
context.Persons.AddObject(person);
context.SaveChanges();
So my problem is as follows...for a project we are bulding an api using RavenDb and Nancy. So my question is about unit testing...we use embeded db, which runs in memory as suggested many times. , how to proper unit test end points. For example, we have a endpoint create account. For that we need to have a user so he can create account. What is the best way to simulate that?
Currently we do it like this:
[Test]
public void UserCanAddAccountToCompany()
{
var user =
new User
{
Name = Guid.NewGuid().ToString(),
Email = Guid.NewGuid().ToString(),
Pwd = "password",
CompanyReference = new CompanyReference { Id = Guid.NewGuid().ToString(), Name = Guid.NewGuid().ToString() }
};
var response = new TestBrowser<User>("User/SignUp", user).Response;
var paramUserAccount = new ParamUserAccount()
{
User = response.Body.DeserializeJson<Result>().User,
Account = new Account() { Name = Guid.NewGuid().ToString() }
};
var response2 = new TestBrowser<ParamUserAccount>("account/create", paramUserAccount).Response;
var res = response2.Body.DeserializeJson<Result>();
Assert.NotNull(res.Account.Id);
Assert.NotNull(res.Account.Name);
}
So we create a user , call user signup end point and then take the params from response and call creat accoutn end point. The obvious problem with this approach is that if you do a change in signup endpoint and for some reason you break it, all tests like this will fail.
So my question is...what's the right approach on that?
You are intergration testing, not unit testing. To properly unit test, you need to test without the db by using mocking objects.