How to unit test a transaction-wrapped function in Prisma? - unit-testing

I'm very new to Prisma and NestJS. I have the following transaction-wrapped function that I want to unit test by mocking the internal function reserveSingleBook.
This is my code (it does a few actions in a for loop and I want all those actions to be successful before committing the transaction).
async reserveBooks(reserveBooksDto: ReserveBoksDto): Promise<SuccessfulReservationResponse> {
return await this.prisma.$transaction(async (prisma) => {
const reservedBooksResponse = new SuccessfulReservationResponse();
for (const reservation of reserveBooksDto.reservations){
const bookBucket = await this.reserveSingleBook(
prisma,
reserveBooksDto.libraryId,
reservation,
);
const successfulReservation = new SuccessfulReservation();
successfulReservation.book_bucket_id = bookBucket.id;
successfulReservation.units_reserved = reservation.units;
reservedBookssResponse.successful_reservations.push(successfulReservation);
}
return reservedBooksResponse
})
}
This is how I'm currently unit testing it:
it('should throw an exception when at least a parent book does not exist', async () => {
// Arrange
const nonExistingParentBookId = 'non-existing-parentbook-id';
const existingParentBookId = 'existing-parent-book-id';
const mock = jest.spyOn(service, 'reserveSingleBook');
mock
.mockResolvedValueOnce(
{
parentBookId: existingParentBookId,
reserved: 10,
used: 0,
} as BookBucket
)
.mockRejectedValueOnce(new NotFoundException())
const reserveBooksDto = new ReserveBooksDto();
reserveBooksDto.library_id= 'abcde';
const firstReservation = new Reservation();
firstReservation.book_id= nonExistingParentBookId;
firstReservation.units = 10;
const secondReservation = new Reservation();
secondReservation.book_id= existingParentBookId;
secondReservation.units = 10;
reserveBooksDto.reservations = [firstReservation, secondReservation];
// Act / Assert
await expect(service.reserveBooks(
reserveBooksDto
)).rejects.toThrowError(NotFoundException);
expect(mock).toBeCalledTimes(2);
mock.mockRestore();
});
The unit test works PERFECTLY if I remove the transaction, since my second mock call returns the NotFoundException, which is then passed on to ReserveBooks.
However, when I have the transaction in place (don't want anything commited if anything fails), I get an 'undefined' resolved in my function call, instead of the exception thrown.
Does anybody know what I might be doing wrong?
Thanks in advance!!

Related

How could I mock the constructor of a node module with jest

I've been learning jest and been doing ok so far but I've come up with something that I don't know how to resolve. I need to mock the #actions/github module and I think I've mocked the methods of the module the right way I think:
const githubListCommentsMock = jest.fn().mockReturnValue(
{
id: 1,
user: {login: 'github-actions[bot]'},
body: 'Code quality reports: Mock value'
})
const githubDeleteCommentMock = jest.fn()
const githubCreateCommentMock = jest.fn()
const githubIssuesMock = { listComments: githubListCommentsMock,
deleteComment: githubDeleteCommentMock,
createComment: githubCreateCommentMock
}
const githubContextMock = {repo:'Mocked Repository'}
jest.mock('#actions/github', () => ({
Github:jest.fn().mockImplementation(() => (
{issues: githubIssuesMock, context: githubContextMock}))
}))
But I have a piece of code on the file I'm testing that instances the github module like this:
const octokit = new github.GitHub(githubToken)
And my test fails when trying to execute the file with the following error:
TypeError: github.GitHub is not a constructor
Also been learning Jest and was having similar challenges. It is a little different to what you describe (and inline with the hydrated github client mentioned by M Mansour in the comments).
// Setup stub Octokit to return from getOctokit.
const StubOctokit = {
search: {
code: jest.fn()
}
}
// Setup fake response from call to Octokit.search.code.
const fakeResponse = {
data: {
total_count: 4
}
}
// Code search should return fakeResponse.
StubOctokit.search.code.mockReturnValueOnce(fakeResponse);
// getOctokit should return the Octokit stub.
github.getOctokit.mockReturnValueOnce(StubOctokit);
I'm sure there are likely much cleaner ways of doing it. After going round in circles for a while trying different approaches I ended up going step by step. Asserting getOctokit to have been called, using mockReturnValueOnce to set it an empty object, using toHaveLastReturnedWith and building up from there until getting the expected result.

How do I get difficulty over time from Kulupu (polkadotjs)?

// Import
import { ApiPromise, WsProvider } from "#polkadot/api";
// Construct
/*
https://rpc.kulupu.network
https://rpc.kulupu.network/ws
https://rpc.kulupu.corepaper.org
https://rpc.kulupu.corepaper.org/ws
*/
(async () => {
//const wsProvider = new WsProvider('wss://rpc.polkadot.io');
const wsProvider = new WsProvider("wss://rpc.kulupu.network/ws");
const api = await ApiPromise.create({ provider: wsProvider });
// Do something
const chain = await api.rpc.system.chain();
console.log(`You are connected to ${chain} !`);
console.log(await api.query.difficulty.pastDifficultiesAndTimestamps.toJSON());
console.log(api.genesisHash.toHex());
})();
The storage item pastDifficultiesAndTimestamps only holds the last 60 blocks worth of data. For getting that information you just need to fix the following:
console.log(await api.query.difficulty.pastDifficultiesAndTimestamps());
If you want to query the difficulty of a blocks in general, a loop like this will work:
let best_block = await api.derive.chain.bestNumber()
// Could be 0, but that is a lot of queries...
let first_block = best_block - 100;
for (let block = first_block; block < best_block; block++) {
let block_hash = await api.rpc.chain.getBlockHash(block);
let difficulty = await api.query.difficulty.currentDifficulty.at(block_hash);
console.log(block, difficulty)
}
Note that this requires an archive node which has informaiton about all the blocks. Otherwise, by default, a node only stores ~256 previous blocks before state pruning cleans things up.
If you want to see how to make a query like this, but much more efficiently, look at my blog post here:
https://www.shawntabrizi.com/substrate/porting-web3-js-to-polkadot-js/

Code Contracts throws MethodAccess Exception when unit testing

I'm not quite sure why, but when I enable runtime contract checking, I'm getting a MethodAccessException during unit testing. I use the Machine.Specifications test framework and the ReSharper/dotCover test runner. When I test my assembly containing code contracts, I get this MethodAccessException:
Machine.Specifications.SpecificationException Should be of type System.ArgumentNullException but is of type System.MethodAccessException
at BusinessLogic.Specifications.When_testing_whether_a_set_of_cart_items_contains_a_product_and_a_null_set_is_passed.<.ctor>b__3()
in CartItemQuerySpecs.cs: line 66
The specification looks like this:
[Subject(typeof(ShoppingCartQueries), "Cart contains product")]
public class When_testing_whether_a_set_of_cart_items_contains_a_product_and_a_null_set_is_passed
: with_fake_shopping_cart_repository
{
Establish context = () => QueryResult = CartItems.AsQueryable().ForUser(UserWithNoProductsInCart);
Because of = () => Thrown = Catch.Exception(() => result = QueryResult.ContainsProduct(100));
It should_throw = () => Thrown.ShouldBeOfType<ArgumentNullException>();
static bool result;
}
The unit under test looks like this:
[Pure]
public static bool ContainsProduct(this IQueryable<CartItem> items, int id)
{
Contract.Requires<ArgumentNullException>(items != null);
return (items.Any(item => item.ProductId.Equals(id)));
}
Why would I get a MethodAccessException?

Using Moq to mock a repository that returns a value

How do I set up my test method on that mocks a repository which accepts an object?
This is what I have so far:
Service.cs
public int AddCountry(string countryName)
{
Country country = new Country();
country.CountryName = countryName;
return geographicsRepository.SaveCountry(country).CountryId;
}
test.cs
[Test]
public void Insert_Country()
{
//Setup
var geographicsRepository = new Mock<IGeographicRepository>();
geographicsRepository.Setup(x => x.SaveCountry(It.Is<Country>(c => c.CountryName == "Jamaica"))); //How do I return a 1 here?
GeographicService geoService = new GeographicService(geographicsRepository.Object);
int id = geoService.AddCountry("Jamaica");
Assert.AreEqual(1, id);
}
SaveCountry(Country country); returns an int.
I need to do 2 things:
First test, I need to tell the setup to return an int of 1.
I need to create a second test Insert_Duplicate_Country_Throws_Exception(). In my Setup, how do I tell the repository to throw an error when I do:
int id = geoService.AddCountry("Jamaica");
int id = geoService.AddCountry("Jamaica");
Framework:
NUnit.
Moq.
ASP.NET MVC - repository pattern.
Your first test should look something like this:
[Test]
public void Insert_Country()
{
Mock<IGeographicRepository> geographicsRepository = new Mock<IGeographicRepository>();
GeographicService geoService = new GeographicService(geographicsRepository.Object);
// Setup Mock
geographicsRepository
.Setup(x => x.SaveCountry(It.IsAny<Country>()))
.Returns(1);
var id = geoService.AddCountry("Jamaica");
Assert.IsInstanceOf<Int32>(id);
Assert.AreEqual(1, id);
geographicsRepository.VerifyAll();
}
The second test should look like this:
[Test]
public void Insert_Duplicate_Country_Throws_Exception()
{
Mock<IGeographicRepository> geographicsRepository = new Mock<IGeographicRepository>();
GeographicService geoService = new GeographicService(geographicsRepository.Object);
// Setup Mock
geographicsRepository
.Setup(x => x.SaveCountry(It.IsAny<Country>()))
.Throws(new MyException());
try
{
var id = geoService.AddCountry("Jamaica");
Assert.Fail("Exception not thrown");
}
catch (MyException)
{
geographicsRepository.VerifyAll();
}
}
I think maybe you are slightly misunderstanding the purpose of testing with mocks in the two scenarios you have supplied.
In the first scenario, you wish to test that 1 is returned when you pass in "Jamaica". This is not a mock test case but a test case for real behaviour as you wish to test a specific input against an expected output i.e. "Jamaica" -> 1. In this situation mocking is more useful to ensure that internally your service calls SaveCountry on the repository with the expected country, and that it returns the value from the call.
Setting up your "SaveCountry" case and then calling "VerifyAll" on your mock is the key. This will assert that "SaveCountry" was indeed called with country "Jamaica", and that the expected value is returned. In this way you have confidence that your service is wired up to your repository as expected.
[Test]
public void adding_country_saves_country()
{
const int ExpectedCountryId = 666;
var mockRepository = new Mock<IGeographicRepository>();
mockRepository.
Setup(x => x.SaveCountry(It.Is<Country>(c => c.CountryName == "Jamaica"))).
Returns(ExpectedCountryId);
GeographicService service= new GeographicService(mockRepository.Object);
int id = service.AddCountry(new Country("Jamaica"));
mockRepo.VerifyAll();
Assert.AreEqual(ExpectedCountryId, id, "Expected country id.");
}
In the second scenario you wish to test that an exception is raised when you attempt to add a duplicate country. There's not much point in doing this with a mock as all you will test is that your mock has behaviour when adding duplicates, not your real implementation.

Moq - How to unit test changes on a reference in a method

Another day , another question. My service layer has the following method
public MatchViewData CreateMatch(string user)
{
var matchViewData = !HasReachedMaxNumberOfMatchesLimit(user) ?
CreateMatchAndAddToRepository(user) :
MatchViewData.NewInstance(new Match(user));
matchViewData.LimitReached = HasReachedMaxNumberOfMatchesLimit(user);
return matchViewData;
}
The method calls the this helper method to create a new match object:
private MatchViewData CreateMatchAndAddToRepository(string user)
{
var match = new Match(user);
MatchRepository.Add(match);
return MatchViewData.NewInstance(match);
}
The repository stores the given match object and sets the id to some value > 0.
public void Add(Match match)
{
Check.Require(match != null);
var numberOfMatchesBefore = Matches.Count;
SetIdPerReflection(match, NextVal());
Matches.Add(match);
Check.Ensure(numberOfMatchesBefore == Matches.Count - 1);
}
The matchviewdata object copies some properties of the the match object (including the id).
My unit test should verify that the resulting viewdata object in the service has an id > 0. To archieve this, i have to mock the repository and the behaviour of the add method. But the service method creates a new match object every time its been called and the add method on the repository updates the referenced match object (there is no need for a return value). I have no idea to solve this with moq.
This is my unit test so far:
[Test]
public void ServiceCreateMatchReturnedMatchViewDataHasNonZeroId()
{
var match = TestUtils.FakePersistentMatch(User, 1);
var repositoryMock = new Mock<IMatchRepository>();
repositoryMock.Setup(
r => r.Add(It.IsAny<Match>())).Callback(() => match.Id = 1);
var serviceFacade = new DefaultServiceFacade(repositoryMock.Object);
var returnedMatch = serviceFacade.CreateMatch(User);
Assert.That(returnedMatch.Id, Is.GreaterThan(0));
}
I tried some other variations - nothing works.
It looks to me your problem is in this line;
repositoryMock.Setup(
r => r.Add(It.IsAny<Match>())).Callback(() => match.Id = 1);
What you're actually doing here is setting the id of the first match object you have declared in your test, NOT the new match created in your service.
Because the Match object you will be supplying to the Repository is created internally, I can't think of an easy way to reference it in your Test method to setup a callback for it. To me, this is a sign you may be trying to test too much in one unit test.
I think you should simply test that the Add method is called and write a separate test to ensure that it works as exepected.
I propose something like this;
[Test]
public void ServiceAddsNewMatchToRepository()
{
var repositoryMock = new Mock<IMatchRepository>();
bool addCalled = false;
repositoryMock
.Expect(r => r.Add(It.Is<Match>(x => x.Id == 0))
.Callback(() => addCalled = true);
var serviceFacade = new DefaultServiceFacade(repositoryMock.Object);
serviceFacade.CreateMatch(User);
Assert.True(addCalled);
}
....
[Test]
public void AddingANewMatchGeneratesANewId()
{
var match = new Match(user);
var matchRepository = new MatchRepository();
var returnedMatch = matchRepository.Add(match);
Assert.That(returnedMatch.Id, Is.GreaterThan(0));
}