Popular New Releases in Unit Testing
googletest
v1.11.0
mocha
v9.2.2
ava
v4.1.0
learn-go-with-tests
Fixed typo in revisiting arrays and slices
jasmine
4.1.0
Popular Libraries in Unit Testing
by google c++
23619 BSD-3-Clause
GoogleTest - Google Testing and Mocking Framework
by mochajs javascript
21297 MIT
โ๏ธ simple, flexible, fun javascript test framework for node.js & the browser
by enzymejs javascript
19677 MIT
JavaScript Testing utilities for React
by avajs javascript
19628 MIT
Node.js test runner that lets you develop with confidence ๐
by sebastianbergmann php
18324 NOASSERTION
The PHP Unit Testing framework.
by quii go
17304 MIT
Learn Go with test-driven development
by jasmine javascript
15320
Simple JavaScript testing framework for browsers and node.js
by catchorg c++
14767 BSL-1.0
A modern, C++-native, test framework for unit-tests, TDD and BDD - using C++14, C++17 and later (C++11 support is in v2.x branch, and C++03 on the Catch1.x branch)
by stretchr go
14231 MIT
A toolkit with common assertions and mocks that plays nicely with the standard library
Trending New libraries in Unit Testing
by defparam python
912 MIT
Smuggler - An HTTP Request Smuggling / Desync testing tool written in Python 3
by se2p python
865 NOASSERTION
The PYthoN General UnIt Test geNerator is a test-generation tool for Python
by microsoft typescript
780 NOASSERTION
Build a cross-browser end-to-end test suite with Playwright.
by jevakallio javascript
524 MIT
A Jest Reporter to group, hide and prettify spammy console warnings
by qiniu go
505 Apache-2.0
A Comprehensive Coverage Testing System for The Go Programming Language
by MarvinJWendt go
385 MIT
Full-featured test framework for Go! Assertions, fuzzing, input testing, output capturing, and much more! ๐
by volument javascript
370 MIT
An extremely fast and simple JavaScript test runner.
by SwiftDocOrg swift
363
An experimental tool for testing Swift example code in documentation.
by google java
259 Apache-2.0
A simple yet powerful parameterized test runner for Java.
Top Authors in Unit Testing
1
19 Libraries
24123
2
11 Libraries
1657
3
10 Libraries
2759
4
10 Libraries
25001
5
9 Libraries
190
6
8 Libraries
5806
7
7 Libraries
311
8
7 Libraries
277
9
6 Libraries
21
10
6 Libraries
2241
1
19 Libraries
24123
2
11 Libraries
1657
3
10 Libraries
2759
4
10 Libraries
25001
5
9 Libraries
190
6
8 Libraries
5806
7
7 Libraries
311
8
7 Libraries
277
9
6 Libraries
21
10
6 Libraries
2241
Trending Kits in Unit Testing
Code coverage is a measure of how much source code was tested. It's often used to guide testing activities and can be used to enhance quality. Good code coverage does not guarantee your software is bug-free, but it does determine that your code has been executed during a test run. The most powerful feature of this unit testing framework is that it can be used to test any kind of software, ranging from libraries to web applications. There are lots of unit testing frameworks for .NET developers, but the most popular and reliable ones are xunit - open source, communityfocused unit testing tool; fluentassertions - Fluent API for asserting the results of unit tests; AutoFixture is designed to make Test-Driven Development more productive and unit tests more refactoring-safe.
Unit testing is a software testing method by which individual units of source code are tested to determine if they are fit for use. A unit is the smallest testable part of an application like functions, classes, procedures, interfaces, etc. In procedural programming, a unit may be an individual program, function, procedure, or even a single line of code. In object-oriented programming (OOP), the smallest unit is a method, which may belong to a base/ super class or abstract class, or derived/ child class. Here are 12 best Java Unit Testing Open Source libraries in 2022 including mockito - popular Mocking framework for unit tests written; powermock - Java framework for unit testing; ArchUnit - a free, simple and extensible library for checking the architecture of your Java code.
Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation. Unit testing can be done manually but is often automated. The goal of unit testing is to isolate each part of the program and show that individual parts are correct in terms of requirements and functionality. A unit test provides a strict, written contract that the piece of code must satisfy. The entire list of the best 17 best Python Unit Testing Open Source libraries including phpunit - The PHP Unit Testing framework; mockery - simple yet flexible PHP mock object framework; atoum - The modern, simple and intuitive PHP unit testing framework.
Unit testing is the first level of software testing where individual units/ components of a software are tested. The purpose is to validate that each unit of the software performs as designed. A unit is the smallest testable part of any software. It usually has one or a few inputs and usually a single output. Python Unit testing frameworks provide a solid base on which to build your test suite for any python codebase. Python Unit testing frameworks also help in automated testing, sharing test cases across multiple interfaces and monitoring code quality. Following are the best 14 best Python Unit Testing Open Source libraries including pynguin - PYthoN General UnIt Test geNerator is a testgeneration tool; unittest-xml-reporting - unit tes based test runner with Ant/JUnit like XML reporting; codepipeline-nested-cfn - CloudFormation templates, CodeBuild build specification & Python scripts to perform unit tests of a nested CloudFormation template.
Unit testing helps developers by allowing them to refactor their code more confidently, catch regressions early on, and prevent bugs from reaching production. This can have significant benefits for software development teams. JavaScript is easy to get started with, it can quickly become a complex language with many quirks and gotchas. The good news is that there are many tools and frameworks that help us handle those complexities. One such tool is unit testing. Weโve analyzed the most popular open-source JavaScript unit testing frameworks in terms of their features, functionality, and community support. We recommend the following 13 best JavaScript Unit Testing libraries including qunit - An easytouse JavaScript unit testing framework; chutzpah - open-source JavaScript test runner; qunit - An easytouse JavaScript Unit Testing framework.
Unit test is a code which ensures that the program logic works as expected by verifying each component of it. The main goal is to isolate each part of the program and show that the individual parts are correct. The most powerful feature of this unit testing framework is that it can be used to test any kind of software, ranging from libraries to web applications. When it comes to Unit Testing in Ruby, there are many open source libraries that you can use. Here we'll focus on the 7 best Unit Testing libraries for Ruby including Riot - a fast, expressive, and contextual ruby unit testing framework; single_test - Rake tasks to invoke single tests/specs with rakish syntax; riot-rails - Riot macros and helpers for Rails application testing
Unit testing is the first level of software testing where individual units/ components of a software are tested. The purpose is to validate that each unit of the software performs as designed. A unit is the smallest testable part of any software. It usually has one or a few inputs and usually a single output. Good code coverage does not guarantee your software is bug-free, but it does determine that your code has been executed during a test run. In this list, we list down 11 best C++ Unit Testing Open Source libraries that you can use in your project. Such as Catch2 - A modern, Cnative, headeronly, test framework for unittests, TDD and BDD using C11, C14, C17 and later; cpputest - CppUTest unit testing and mocking framework for C/C++; unittest-cpp | A lightweight unit testing framework for C++.
Trending Discussions on Unit Testing
assert that all fields in 2 objects are the same c#
Skipping a method execution using Mockito
How to locally unit-test Chainlink's Verifiable Random Function?
How do I give my JSON schema an absolute URL for its $id when I haven't published it yet because it hasn't been tested yet?
How to mock function without class in Kotlin?
What is the 'X' in this insert > X'3C2F756C3E'
Deploying Uniswap v2 / Sushiswap or similar in Brownie, Hardhat or Truffle test suite
How to add unit testing in strapi version 4 using jest?
What exactly is/means "System.IO.Stream+NullStream"?
Unit testing SwiftUI/Combine @Published boolean values
QUESTION
assert that all fields in 2 objects are the same c#
Asked 2022-Apr-08 at 08:50I am doing unit testing, and basically want to check that the data that 2 objects hold is the same
1Assert.AreEqual(object1, object2);
2Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
3
I am searching for the C# equivalent of assertJ
1Assert.AreEqual(object1, object2);
2Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
3Assert.That(object1).isEqualToComparingFieldByField(object2)
4
ANSWER
Answered 2022-Apr-08 at 07:47You could either use records (c# 9 +) or you have to override the Equals method (if you have access and you can change the objects that you're working with). Records example:
1Assert.AreEqual(object1, object2);
2Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
3Assert.That(object1).isEqualToComparingFieldByField(object2)
4var point = new Point(3, 4);
5var point2 = new Point(3, 4);
6
7var test = point.Equals(point2); //this is true
8
9public record Point(int X, int Y);
10
with classes:
1Assert.AreEqual(object1, object2);
2Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
3Assert.That(object1).isEqualToComparingFieldByField(object2)
4var point = new Point(3, 4);
5var point2 = new Point(3, 4);
6
7var test = point.Equals(point2); //this is true
8
9public record Point(int X, int Y);
10public class Point
11{
12 public int X { get; }
13 public int Y { get; }
14
15
16 public override bool Equals(object? obj)
17 {
18 if (obj == null)
19 return false;
20
21 return obj is Point point && (point.X == X && point.Y == Y);
22 }
23
24 public override int GetHashCode()
25 {
26 return HashCode.Combine(X, Y);
27 }
28}
29
if you are not allowed to touch the implementation, then you could use serialization and compare the strings:
1Assert.AreEqual(object1, object2);
2Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
3Assert.That(object1).isEqualToComparingFieldByField(object2)
4var point = new Point(3, 4);
5var point2 = new Point(3, 4);
6
7var test = point.Equals(point2); //this is true
8
9public record Point(int X, int Y);
10public class Point
11{
12 public int X { get; }
13 public int Y { get; }
14
15
16 public override bool Equals(object? obj)
17 {
18 if (obj == null)
19 return false;
20
21 return obj is Point point && (point.X == X && point.Y == Y);
22 }
23
24 public override int GetHashCode()
25 {
26 return HashCode.Combine(X, Y);
27 }
28}
29var obj1Str = JsonConvert.SerializeObject(object1);
30var obj2Str = JsonConvert.SerializeObject(object2);
31Assert.Equal(obj1Str, obj2Str);
32
using Newtonsoft.Json nuget
QUESTION
Skipping a method execution using Mockito
Asked 2022-Mar-21 at 09:41Iโm using Mockito for unit testing and I want to skip the execution of a method.
I referred to this ticket Skip execution of a line using Mockito. Here, I assume doSomeTask() and createLink() methods are in different classes. But in my case, both the methods are in the same class (ActualClass.java).
1//Actual Class
2
3public class ActualClass{
4
5 //The method being tested
6 public void method(){
7 //some business logic
8 validate(param1, param2);
9
10 //some business logic
11 }
12
13 public void validate(arg1, arg2){
14 //do something
15 }
16}
17
18//Test class
19
20public class ActualClassTest{
21
22 @InjectMocks
23 private ActualClass actualClassMock;
24
25 @Test
26 public void test_method(){
27
28 ActualClass actualClass = new ActualClass();
29 ActualClass spyActualClass = Mockito.spy(actualClass);
30
31 // validate method creates a null pointer exception, due to some real time data fetch from elastic
32
33 doNothing().when(spyActualClass).validate(Mockito.any(), Mockito.any());
34 actualClassMock.method();
35 }
36}
37
Since there arises a null pointer exception when the validate method is executed, Iโm trying to skip the method call by spying the ActualClass object as stated in the ticket I mentioned above. Still, the issue is not resolve. The validate method gets executed and creates a null pointer exception due to which the actual test method cannot be covered.
So, how do I skip the execution of the validate() method that is within the same class.
ANSWER
Answered 2022-Mar-04 at 09:36You must always use your spy class when calling method()
.
1//Actual Class
2
3public class ActualClass{
4
5 //The method being tested
6 public void method(){
7 //some business logic
8 validate(param1, param2);
9
10 //some business logic
11 }
12
13 public void validate(arg1, arg2){
14 //do something
15 }
16}
17
18//Test class
19
20public class ActualClassTest{
21
22 @InjectMocks
23 private ActualClass actualClassMock;
24
25 @Test
26 public void test_method(){
27
28 ActualClass actualClass = new ActualClass();
29 ActualClass spyActualClass = Mockito.spy(actualClass);
30
31 // validate method creates a null pointer exception, due to some real time data fetch from elastic
32
33 doNothing().when(spyActualClass).validate(Mockito.any(), Mockito.any());
34 actualClassMock.method();
35 }
36}
37 @Test
38 public void test_method(){
39 ActualClass spyActualClass = Mockito.spy(actualClassMock);
40
41 doNothing().when(spyActualClass).validate(Mockito.any(), Mockito.any());
42 spyActualClass.method();
43 }
44
In practice, instead of
1//Actual Class
2
3public class ActualClass{
4
5 //The method being tested
6 public void method(){
7 //some business logic
8 validate(param1, param2);
9
10 //some business logic
11 }
12
13 public void validate(arg1, arg2){
14 //do something
15 }
16}
17
18//Test class
19
20public class ActualClassTest{
21
22 @InjectMocks
23 private ActualClass actualClassMock;
24
25 @Test
26 public void test_method(){
27
28 ActualClass actualClass = new ActualClass();
29 ActualClass spyActualClass = Mockito.spy(actualClass);
30
31 // validate method creates a null pointer exception, due to some real time data fetch from elastic
32
33 doNothing().when(spyActualClass).validate(Mockito.any(), Mockito.any());
34 actualClassMock.method();
35 }
36}
37 @Test
38 public void test_method(){
39 ActualClass spyActualClass = Mockito.spy(actualClassMock);
40
41 doNothing().when(spyActualClass).validate(Mockito.any(), Mockito.any());
42 spyActualClass.method();
43 }
44actualClassMock.method();
45
you must use
1//Actual Class
2
3public class ActualClass{
4
5 //The method being tested
6 public void method(){
7 //some business logic
8 validate(param1, param2);
9
10 //some business logic
11 }
12
13 public void validate(arg1, arg2){
14 //do something
15 }
16}
17
18//Test class
19
20public class ActualClassTest{
21
22 @InjectMocks
23 private ActualClass actualClassMock;
24
25 @Test
26 public void test_method(){
27
28 ActualClass actualClass = new ActualClass();
29 ActualClass spyActualClass = Mockito.spy(actualClass);
30
31 // validate method creates a null pointer exception, due to some real time data fetch from elastic
32
33 doNothing().when(spyActualClass).validate(Mockito.any(), Mockito.any());
34 actualClassMock.method();
35 }
36}
37 @Test
38 public void test_method(){
39 ActualClass spyActualClass = Mockito.spy(actualClassMock);
40
41 doNothing().when(spyActualClass).validate(Mockito.any(), Mockito.any());
42 spyActualClass.method();
43 }
44actualClassMock.method();
45spyActualClass.method();
46
QUESTION
How to locally unit-test Chainlink's Verifiable Random Function?
Asked 2022-Mar-08 at 04:12While trying to set up a basic self-hosted unit testing environment (and CI) that tests this Chainlink VRF random number contract, I am experiencing slight difficulties in how to simulate any relevant blockchains/testnets locally.
For example, I found this repository that tests Chainlinks VRF. However, for default deployment it suggests/requires a free KOVAN_RPC_URL
e.g. from Infura's site and even for "local deployment" it suggests/requires a free MAINNET_RPC_URL
from e.g. Alchemy's site.
I adopted a unit test environment from the waffle framework which is described as:
Filestructure1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10
AmIRichAlready.sol
1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10pragma solidity ^0.6.2;
11
12interface IERC20 {
13 function balanceOf(address account) external view returns (uint256);
14}
15
16contract AmIRichAlready {
17 IERC20 private tokenContract;
18 uint public richness = 1000000 * 10 ** 18;
19
20 constructor (IERC20 _tokenContract) public {
21 tokenContract = _tokenContract;
22 }
23
24 function check() public view returns (bool) {
25 uint balance = tokenContract.balanceOf(msg.sender);
26 return balance > richness;
27 }
28
29 // IS THIS NEEDED???
30 function setRichness(uint256 _richness) public {
31 richness = _richness;
32 }
33}
34
The RandomNumberConsumer.sol
filecontent is already on stackexange over here.
AmIRichAlready.test.ts
1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10pragma solidity ^0.6.2;
11
12interface IERC20 {
13 function balanceOf(address account) external view returns (uint256);
14}
15
16contract AmIRichAlready {
17 IERC20 private tokenContract;
18 uint public richness = 1000000 * 10 ** 18;
19
20 constructor (IERC20 _tokenContract) public {
21 tokenContract = _tokenContract;
22 }
23
24 function check() public view returns (bool) {
25 uint balance = tokenContract.balanceOf(msg.sender);
26 return balance > richness;
27 }
28
29 // IS THIS NEEDED???
30 function setRichness(uint256 _richness) public {
31 richness = _richness;
32 }
33}
34import {expect, use} from 'chai';
35import {Contract, utils, Wallet} from 'ethers';
36import {deployContract, deployMockContract, MockProvider, solidity} from 'ethereum-waffle';
37
38import IERC20 from '../build/IERC20.json';
39import AmIRichAlready from '../build/AmIRichAlready.json';
40
41use(solidity);
42
43describe('Am I Rich Already', () => {
44 let mockERC20: Contract;
45 let contract: Contract;
46 let vrfContract: Contract;
47 let wallet: Wallet;
48
49 beforeEach(async () => {
50 [wallet] = new MockProvider().getWallets();
51 mockERC20 = await deployMockContract(wallet, IERC20.abi);
52 contract = await deployContract(wallet, AmIRichAlready, [mockERC20.address]);
53 vrfContract = await deployContract(wallet, RandomNumberConsumer);
54 });
55
56 it('checks if contract called balanceOf with certain wallet on the ERC20 token', async () => {
57 await mockERC20.mock.balanceOf
58 .withArgs(wallet.address)
59 .returns(utils.parseEther('999999'));
60 await contract.check();
61 expect('balanceOf').to.be.calledOnContractWith(mockERC20, [wallet.address]);
62 });
63
64 it('returns false if the wallet has less than 1000000 coins', async () => {
65 await mockERC20.mock.balanceOf
66 .withArgs(wallet.address)
67 .returns(utils.parseEther('999999'));
68 expect(await contract.check()).to.be.equal(false);
69 });
70
71 it('returns true if the wallet has at least 1000000 coins', async () => {
72 await mockERC20.mock.balanceOf
73 .withArgs(wallet.address)
74 .returns(utils.parseEther('1000000'));
75 expect(await contract.check()).to.be.equal(false);
76 });
77});
78
mocha.opts
1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10pragma solidity ^0.6.2;
11
12interface IERC20 {
13 function balanceOf(address account) external view returns (uint256);
14}
15
16contract AmIRichAlready {
17 IERC20 private tokenContract;
18 uint public richness = 1000000 * 10 ** 18;
19
20 constructor (IERC20 _tokenContract) public {
21 tokenContract = _tokenContract;
22 }
23
24 function check() public view returns (bool) {
25 uint balance = tokenContract.balanceOf(msg.sender);
26 return balance > richness;
27 }
28
29 // IS THIS NEEDED???
30 function setRichness(uint256 _richness) public {
31 richness = _richness;
32 }
33}
34import {expect, use} from 'chai';
35import {Contract, utils, Wallet} from 'ethers';
36import {deployContract, deployMockContract, MockProvider, solidity} from 'ethereum-waffle';
37
38import IERC20 from '../build/IERC20.json';
39import AmIRichAlready from '../build/AmIRichAlready.json';
40
41use(solidity);
42
43describe('Am I Rich Already', () => {
44 let mockERC20: Contract;
45 let contract: Contract;
46 let vrfContract: Contract;
47 let wallet: Wallet;
48
49 beforeEach(async () => {
50 [wallet] = new MockProvider().getWallets();
51 mockERC20 = await deployMockContract(wallet, IERC20.abi);
52 contract = await deployContract(wallet, AmIRichAlready, [mockERC20.address]);
53 vrfContract = await deployContract(wallet, RandomNumberConsumer);
54 });
55
56 it('checks if contract called balanceOf with certain wallet on the ERC20 token', async () => {
57 await mockERC20.mock.balanceOf
58 .withArgs(wallet.address)
59 .returns(utils.parseEther('999999'));
60 await contract.check();
61 expect('balanceOf').to.be.calledOnContractWith(mockERC20, [wallet.address]);
62 });
63
64 it('returns false if the wallet has less than 1000000 coins', async () => {
65 await mockERC20.mock.balanceOf
66 .withArgs(wallet.address)
67 .returns(utils.parseEther('999999'));
68 expect(await contract.check()).to.be.equal(false);
69 });
70
71 it('returns true if the wallet has at least 1000000 coins', async () => {
72 await mockERC20.mock.balanceOf
73 .withArgs(wallet.address)
74 .returns(utils.parseEther('1000000'));
75 expect(await contract.check()).to.be.equal(false);
76 });
77});
78-r ts-node/register/transpile-only
79--timeout 50000
80--no-warnings
81test/**/*.test.{js,ts}
82
package.json
1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10pragma solidity ^0.6.2;
11
12interface IERC20 {
13 function balanceOf(address account) external view returns (uint256);
14}
15
16contract AmIRichAlready {
17 IERC20 private tokenContract;
18 uint public richness = 1000000 * 10 ** 18;
19
20 constructor (IERC20 _tokenContract) public {
21 tokenContract = _tokenContract;
22 }
23
24 function check() public view returns (bool) {
25 uint balance = tokenContract.balanceOf(msg.sender);
26 return balance > richness;
27 }
28
29 // IS THIS NEEDED???
30 function setRichness(uint256 _richness) public {
31 richness = _richness;
32 }
33}
34import {expect, use} from 'chai';
35import {Contract, utils, Wallet} from 'ethers';
36import {deployContract, deployMockContract, MockProvider, solidity} from 'ethereum-waffle';
37
38import IERC20 from '../build/IERC20.json';
39import AmIRichAlready from '../build/AmIRichAlready.json';
40
41use(solidity);
42
43describe('Am I Rich Already', () => {
44 let mockERC20: Contract;
45 let contract: Contract;
46 let vrfContract: Contract;
47 let wallet: Wallet;
48
49 beforeEach(async () => {
50 [wallet] = new MockProvider().getWallets();
51 mockERC20 = await deployMockContract(wallet, IERC20.abi);
52 contract = await deployContract(wallet, AmIRichAlready, [mockERC20.address]);
53 vrfContract = await deployContract(wallet, RandomNumberConsumer);
54 });
55
56 it('checks if contract called balanceOf with certain wallet on the ERC20 token', async () => {
57 await mockERC20.mock.balanceOf
58 .withArgs(wallet.address)
59 .returns(utils.parseEther('999999'));
60 await contract.check();
61 expect('balanceOf').to.be.calledOnContractWith(mockERC20, [wallet.address]);
62 });
63
64 it('returns false if the wallet has less than 1000000 coins', async () => {
65 await mockERC20.mock.balanceOf
66 .withArgs(wallet.address)
67 .returns(utils.parseEther('999999'));
68 expect(await contract.check()).to.be.equal(false);
69 });
70
71 it('returns true if the wallet has at least 1000000 coins', async () => {
72 await mockERC20.mock.balanceOf
73 .withArgs(wallet.address)
74 .returns(utils.parseEther('1000000'));
75 expect(await contract.check()).to.be.equal(false);
76 });
77});
78-r ts-node/register/transpile-only
79--timeout 50000
80--no-warnings
81test/**/*.test.{js,ts}
82{
83 "name": "example-dynamic-mocking-and-testing-calls",
84 "version": "1.0.0",
85 "main": "index.js",
86 "license": "MIT",
87 "scripts": {
88 "test": "export NODE_ENV=test && mocha",
89 "build": "waffle",
90 "lint": "eslint '{src,test}/**/*.ts'",
91 "lint:fix": "eslint --fix '{src,test}/**/*.ts'"
92 },
93 "devDependencies": {
94 "@openzeppelin/contracts": "^4.3.1",
95 "@types/chai": "^4.2.3",
96 "@types/mocha": "^5.2.7",
97 "@typescript-eslint/eslint-plugin": "^2.30.0",
98 "@typescript-eslint/parser": "^2.30.0",
99 "chai": "^4.3.4",
100 "eslint": "^6.8.0",
101 "eslint-plugin-import": "^2.20.2",
102 "ethereum-waffle": "^3.4.0",
103 "ethers": "^5.0.17",
104 "mocha": "^7.2.0",
105 "ts-node": "^8.9.1",
106 "typescript": "^3.8.3"
107 }
108}
109
tsconfig.json
1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10pragma solidity ^0.6.2;
11
12interface IERC20 {
13 function balanceOf(address account) external view returns (uint256);
14}
15
16contract AmIRichAlready {
17 IERC20 private tokenContract;
18 uint public richness = 1000000 * 10 ** 18;
19
20 constructor (IERC20 _tokenContract) public {
21 tokenContract = _tokenContract;
22 }
23
24 function check() public view returns (bool) {
25 uint balance = tokenContract.balanceOf(msg.sender);
26 return balance > richness;
27 }
28
29 // IS THIS NEEDED???
30 function setRichness(uint256 _richness) public {
31 richness = _richness;
32 }
33}
34import {expect, use} from 'chai';
35import {Contract, utils, Wallet} from 'ethers';
36import {deployContract, deployMockContract, MockProvider, solidity} from 'ethereum-waffle';
37
38import IERC20 from '../build/IERC20.json';
39import AmIRichAlready from '../build/AmIRichAlready.json';
40
41use(solidity);
42
43describe('Am I Rich Already', () => {
44 let mockERC20: Contract;
45 let contract: Contract;
46 let vrfContract: Contract;
47 let wallet: Wallet;
48
49 beforeEach(async () => {
50 [wallet] = new MockProvider().getWallets();
51 mockERC20 = await deployMockContract(wallet, IERC20.abi);
52 contract = await deployContract(wallet, AmIRichAlready, [mockERC20.address]);
53 vrfContract = await deployContract(wallet, RandomNumberConsumer);
54 });
55
56 it('checks if contract called balanceOf with certain wallet on the ERC20 token', async () => {
57 await mockERC20.mock.balanceOf
58 .withArgs(wallet.address)
59 .returns(utils.parseEther('999999'));
60 await contract.check();
61 expect('balanceOf').to.be.calledOnContractWith(mockERC20, [wallet.address]);
62 });
63
64 it('returns false if the wallet has less than 1000000 coins', async () => {
65 await mockERC20.mock.balanceOf
66 .withArgs(wallet.address)
67 .returns(utils.parseEther('999999'));
68 expect(await contract.check()).to.be.equal(false);
69 });
70
71 it('returns true if the wallet has at least 1000000 coins', async () => {
72 await mockERC20.mock.balanceOf
73 .withArgs(wallet.address)
74 .returns(utils.parseEther('1000000'));
75 expect(await contract.check()).to.be.equal(false);
76 });
77});
78-r ts-node/register/transpile-only
79--timeout 50000
80--no-warnings
81test/**/*.test.{js,ts}
82{
83 "name": "example-dynamic-mocking-and-testing-calls",
84 "version": "1.0.0",
85 "main": "index.js",
86 "license": "MIT",
87 "scripts": {
88 "test": "export NODE_ENV=test && mocha",
89 "build": "waffle",
90 "lint": "eslint '{src,test}/**/*.ts'",
91 "lint:fix": "eslint --fix '{src,test}/**/*.ts'"
92 },
93 "devDependencies": {
94 "@openzeppelin/contracts": "^4.3.1",
95 "@types/chai": "^4.2.3",
96 "@types/mocha": "^5.2.7",
97 "@typescript-eslint/eslint-plugin": "^2.30.0",
98 "@typescript-eslint/parser": "^2.30.0",
99 "chai": "^4.3.4",
100 "eslint": "^6.8.0",
101 "eslint-plugin-import": "^2.20.2",
102 "ethereum-waffle": "^3.4.0",
103 "ethers": "^5.0.17",
104 "mocha": "^7.2.0",
105 "ts-node": "^8.9.1",
106 "typescript": "^3.8.3"
107 }
108}
109{
110 "compilerOptions": {
111 "declaration": true,
112 "esModuleInterop": true,
113 "lib": [
114 "ES2018"
115 ],
116 "module": "CommonJS",
117 "moduleResolution": "node",
118 "outDir": "dist",
119 "resolveJsonModule": true,
120 "skipLibCheck": true,
121 "strict": true,
122 "target": "ES2018"
123 }
124
125 // custom test in vrfContract
126 it('Tests if a random number is returned', async () => {
127 expect(await vrfContract.getRandomNumber()).to.be.equal(7);
128 });
129}
130
waffle.json
1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10pragma solidity ^0.6.2;
11
12interface IERC20 {
13 function balanceOf(address account) external view returns (uint256);
14}
15
16contract AmIRichAlready {
17 IERC20 private tokenContract;
18 uint public richness = 1000000 * 10 ** 18;
19
20 constructor (IERC20 _tokenContract) public {
21 tokenContract = _tokenContract;
22 }
23
24 function check() public view returns (bool) {
25 uint balance = tokenContract.balanceOf(msg.sender);
26 return balance > richness;
27 }
28
29 // IS THIS NEEDED???
30 function setRichness(uint256 _richness) public {
31 richness = _richness;
32 }
33}
34import {expect, use} from 'chai';
35import {Contract, utils, Wallet} from 'ethers';
36import {deployContract, deployMockContract, MockProvider, solidity} from 'ethereum-waffle';
37
38import IERC20 from '../build/IERC20.json';
39import AmIRichAlready from '../build/AmIRichAlready.json';
40
41use(solidity);
42
43describe('Am I Rich Already', () => {
44 let mockERC20: Contract;
45 let contract: Contract;
46 let vrfContract: Contract;
47 let wallet: Wallet;
48
49 beforeEach(async () => {
50 [wallet] = new MockProvider().getWallets();
51 mockERC20 = await deployMockContract(wallet, IERC20.abi);
52 contract = await deployContract(wallet, AmIRichAlready, [mockERC20.address]);
53 vrfContract = await deployContract(wallet, RandomNumberConsumer);
54 });
55
56 it('checks if contract called balanceOf with certain wallet on the ERC20 token', async () => {
57 await mockERC20.mock.balanceOf
58 .withArgs(wallet.address)
59 .returns(utils.parseEther('999999'));
60 await contract.check();
61 expect('balanceOf').to.be.calledOnContractWith(mockERC20, [wallet.address]);
62 });
63
64 it('returns false if the wallet has less than 1000000 coins', async () => {
65 await mockERC20.mock.balanceOf
66 .withArgs(wallet.address)
67 .returns(utils.parseEther('999999'));
68 expect(await contract.check()).to.be.equal(false);
69 });
70
71 it('returns true if the wallet has at least 1000000 coins', async () => {
72 await mockERC20.mock.balanceOf
73 .withArgs(wallet.address)
74 .returns(utils.parseEther('1000000'));
75 expect(await contract.check()).to.be.equal(false);
76 });
77});
78-r ts-node/register/transpile-only
79--timeout 50000
80--no-warnings
81test/**/*.test.{js,ts}
82{
83 "name": "example-dynamic-mocking-and-testing-calls",
84 "version": "1.0.0",
85 "main": "index.js",
86 "license": "MIT",
87 "scripts": {
88 "test": "export NODE_ENV=test && mocha",
89 "build": "waffle",
90 "lint": "eslint '{src,test}/**/*.ts'",
91 "lint:fix": "eslint --fix '{src,test}/**/*.ts'"
92 },
93 "devDependencies": {
94 "@openzeppelin/contracts": "^4.3.1",
95 "@types/chai": "^4.2.3",
96 "@types/mocha": "^5.2.7",
97 "@typescript-eslint/eslint-plugin": "^2.30.0",
98 "@typescript-eslint/parser": "^2.30.0",
99 "chai": "^4.3.4",
100 "eslint": "^6.8.0",
101 "eslint-plugin-import": "^2.20.2",
102 "ethereum-waffle": "^3.4.0",
103 "ethers": "^5.0.17",
104 "mocha": "^7.2.0",
105 "ts-node": "^8.9.1",
106 "typescript": "^3.8.3"
107 }
108}
109{
110 "compilerOptions": {
111 "declaration": true,
112 "esModuleInterop": true,
113 "lib": [
114 "ES2018"
115 ],
116 "module": "CommonJS",
117 "moduleResolution": "node",
118 "outDir": "dist",
119 "resolveJsonModule": true,
120 "skipLibCheck": true,
121 "strict": true,
122 "target": "ES2018"
123 }
124
125 // custom test in vrfContract
126 it('Tests if a random number is returned', async () => {
127 expect(await vrfContract.getRandomNumber()).to.be.equal(7);
128 });
129}
130{
131 "compilerType": "solcjs",
132 "compilerVersion": "0.6.2",
133 "sourceDirectory": "./src",
134 "outputDirectory": "./build"
135}
136
The yarn.lock
file content is a bit large, and it's auto-generated, so you can find it on the Waffle framework repository. Similarly, the package.json
can be found here, in the same repository.
One can also simply clone the repo with the specified filestructure here, and run the tests with the following commands:
1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10pragma solidity ^0.6.2;
11
12interface IERC20 {
13 function balanceOf(address account) external view returns (uint256);
14}
15
16contract AmIRichAlready {
17 IERC20 private tokenContract;
18 uint public richness = 1000000 * 10 ** 18;
19
20 constructor (IERC20 _tokenContract) public {
21 tokenContract = _tokenContract;
22 }
23
24 function check() public view returns (bool) {
25 uint balance = tokenContract.balanceOf(msg.sender);
26 return balance > richness;
27 }
28
29 // IS THIS NEEDED???
30 function setRichness(uint256 _richness) public {
31 richness = _richness;
32 }
33}
34import {expect, use} from 'chai';
35import {Contract, utils, Wallet} from 'ethers';
36import {deployContract, deployMockContract, MockProvider, solidity} from 'ethereum-waffle';
37
38import IERC20 from '../build/IERC20.json';
39import AmIRichAlready from '../build/AmIRichAlready.json';
40
41use(solidity);
42
43describe('Am I Rich Already', () => {
44 let mockERC20: Contract;
45 let contract: Contract;
46 let vrfContract: Contract;
47 let wallet: Wallet;
48
49 beforeEach(async () => {
50 [wallet] = new MockProvider().getWallets();
51 mockERC20 = await deployMockContract(wallet, IERC20.abi);
52 contract = await deployContract(wallet, AmIRichAlready, [mockERC20.address]);
53 vrfContract = await deployContract(wallet, RandomNumberConsumer);
54 });
55
56 it('checks if contract called balanceOf with certain wallet on the ERC20 token', async () => {
57 await mockERC20.mock.balanceOf
58 .withArgs(wallet.address)
59 .returns(utils.parseEther('999999'));
60 await contract.check();
61 expect('balanceOf').to.be.calledOnContractWith(mockERC20, [wallet.address]);
62 });
63
64 it('returns false if the wallet has less than 1000000 coins', async () => {
65 await mockERC20.mock.balanceOf
66 .withArgs(wallet.address)
67 .returns(utils.parseEther('999999'));
68 expect(await contract.check()).to.be.equal(false);
69 });
70
71 it('returns true if the wallet has at least 1000000 coins', async () => {
72 await mockERC20.mock.balanceOf
73 .withArgs(wallet.address)
74 .returns(utils.parseEther('1000000'));
75 expect(await contract.check()).to.be.equal(false);
76 });
77});
78-r ts-node/register/transpile-only
79--timeout 50000
80--no-warnings
81test/**/*.test.{js,ts}
82{
83 "name": "example-dynamic-mocking-and-testing-calls",
84 "version": "1.0.0",
85 "main": "index.js",
86 "license": "MIT",
87 "scripts": {
88 "test": "export NODE_ENV=test && mocha",
89 "build": "waffle",
90 "lint": "eslint '{src,test}/**/*.ts'",
91 "lint:fix": "eslint --fix '{src,test}/**/*.ts'"
92 },
93 "devDependencies": {
94 "@openzeppelin/contracts": "^4.3.1",
95 "@types/chai": "^4.2.3",
96 "@types/mocha": "^5.2.7",
97 "@typescript-eslint/eslint-plugin": "^2.30.0",
98 "@typescript-eslint/parser": "^2.30.0",
99 "chai": "^4.3.4",
100 "eslint": "^6.8.0",
101 "eslint-plugin-import": "^2.20.2",
102 "ethereum-waffle": "^3.4.0",
103 "ethers": "^5.0.17",
104 "mocha": "^7.2.0",
105 "ts-node": "^8.9.1",
106 "typescript": "^3.8.3"
107 }
108}
109{
110 "compilerOptions": {
111 "declaration": true,
112 "esModuleInterop": true,
113 "lib": [
114 "ES2018"
115 ],
116 "module": "CommonJS",
117 "moduleResolution": "node",
118 "outDir": "dist",
119 "resolveJsonModule": true,
120 "skipLibCheck": true,
121 "strict": true,
122 "target": "ES2018"
123 }
124
125 // custom test in vrfContract
126 it('Tests if a random number is returned', async () => {
127 expect(await vrfContract.getRandomNumber()).to.be.equal(7);
128 });
129}
130{
131 "compilerType": "solcjs",
132 "compilerVersion": "0.6.2",
133 "sourceDirectory": "./src",
134 "outputDirectory": "./build"
135}
136git clone git@github.com:a-t-2/chainlink.git
137git clone git@github.com:a-t-2/test_vrf3.git
138cd test_vrf3
139sudo apt install npm
140npm install
141npm audit fix
142npm install --save-dev ethereum-waffle
143npm install @openzeppelin/contracts -D
144npm i chai -D
145npm i mocha -D
146rm -r build
147npx waffle
148npx mocha
149npm test
150
This will test the AmIRichAlready.sol
file and output:
1src____AmIRichAlready.sol
2 |____RandomNumberConsumer.sol
3 |
4test____AmIRichAlready.test.ts
5 |____mocha.opts
6package.json
7tsconfig.json
8waffle.json
9yarn.lock
10pragma solidity ^0.6.2;
11
12interface IERC20 {
13 function balanceOf(address account) external view returns (uint256);
14}
15
16contract AmIRichAlready {
17 IERC20 private tokenContract;
18 uint public richness = 1000000 * 10 ** 18;
19
20 constructor (IERC20 _tokenContract) public {
21 tokenContract = _tokenContract;
22 }
23
24 function check() public view returns (bool) {
25 uint balance = tokenContract.balanceOf(msg.sender);
26 return balance > richness;
27 }
28
29 // IS THIS NEEDED???
30 function setRichness(uint256 _richness) public {
31 richness = _richness;
32 }
33}
34import {expect, use} from 'chai';
35import {Contract, utils, Wallet} from 'ethers';
36import {deployContract, deployMockContract, MockProvider, solidity} from 'ethereum-waffle';
37
38import IERC20 from '../build/IERC20.json';
39import AmIRichAlready from '../build/AmIRichAlready.json';
40
41use(solidity);
42
43describe('Am I Rich Already', () => {
44 let mockERC20: Contract;
45 let contract: Contract;
46 let vrfContract: Contract;
47 let wallet: Wallet;
48
49 beforeEach(async () => {
50 [wallet] = new MockProvider().getWallets();
51 mockERC20 = await deployMockContract(wallet, IERC20.abi);
52 contract = await deployContract(wallet, AmIRichAlready, [mockERC20.address]);
53 vrfContract = await deployContract(wallet, RandomNumberConsumer);
54 });
55
56 it('checks if contract called balanceOf with certain wallet on the ERC20 token', async () => {
57 await mockERC20.mock.balanceOf
58 .withArgs(wallet.address)
59 .returns(utils.parseEther('999999'));
60 await contract.check();
61 expect('balanceOf').to.be.calledOnContractWith(mockERC20, [wallet.address]);
62 });
63
64 it('returns false if the wallet has less than 1000000 coins', async () => {
65 await mockERC20.mock.balanceOf
66 .withArgs(wallet.address)
67 .returns(utils.parseEther('999999'));
68 expect(await contract.check()).to.be.equal(false);
69 });
70
71 it('returns true if the wallet has at least 1000000 coins', async () => {
72 await mockERC20.mock.balanceOf
73 .withArgs(wallet.address)
74 .returns(utils.parseEther('1000000'));
75 expect(await contract.check()).to.be.equal(false);
76 });
77});
78-r ts-node/register/transpile-only
79--timeout 50000
80--no-warnings
81test/**/*.test.{js,ts}
82{
83 "name": "example-dynamic-mocking-and-testing-calls",
84 "version": "1.0.0",
85 "main": "index.js",
86 "license": "MIT",
87 "scripts": {
88 "test": "export NODE_ENV=test && mocha",
89 "build": "waffle",
90 "lint": "eslint '{src,test}/**/*.ts'",
91 "lint:fix": "eslint --fix '{src,test}/**/*.ts'"
92 },
93 "devDependencies": {
94 "@openzeppelin/contracts": "^4.3.1",
95 "@types/chai": "^4.2.3",
96 "@types/mocha": "^5.2.7",
97 "@typescript-eslint/eslint-plugin": "^2.30.0",
98 "@typescript-eslint/parser": "^2.30.0",
99 "chai": "^4.3.4",
100 "eslint": "^6.8.0",
101 "eslint-plugin-import": "^2.20.2",
102 "ethereum-waffle": "^3.4.0",
103 "ethers": "^5.0.17",
104 "mocha": "^7.2.0",
105 "ts-node": "^8.9.1",
106 "typescript": "^3.8.3"
107 }
108}
109{
110 "compilerOptions": {
111 "declaration": true,
112 "esModuleInterop": true,
113 "lib": [
114 "ES2018"
115 ],
116 "module": "CommonJS",
117 "moduleResolution": "node",
118 "outDir": "dist",
119 "resolveJsonModule": true,
120 "skipLibCheck": true,
121 "strict": true,
122 "target": "ES2018"
123 }
124
125 // custom test in vrfContract
126 it('Tests if a random number is returned', async () => {
127 expect(await vrfContract.getRandomNumber()).to.be.equal(7);
128 });
129}
130{
131 "compilerType": "solcjs",
132 "compilerVersion": "0.6.2",
133 "sourceDirectory": "./src",
134 "outputDirectory": "./build"
135}
136git clone git@github.com:a-t-2/chainlink.git
137git clone git@github.com:a-t-2/test_vrf3.git
138cd test_vrf3
139sudo apt install npm
140npm install
141npm audit fix
142npm install --save-dev ethereum-waffle
143npm install @openzeppelin/contracts -D
144npm i chai -D
145npm i mocha -D
146rm -r build
147npx waffle
148npx mocha
149npm test
150 Am I Rich Already
151 โ checks if contract called balanceOf with certain wallet on the ERC20 token (249ms)
152 โ returns false if the wallet has less than 1000000 coins (190ms)
153 โ returns true if the wallet has at least 1000000 coins (159ms)
154 Tests if a random number is returned:
155 Error: cannot estimate gas; transaction may fail or may require manual gas limit (error={"name":"RuntimeError","results":{"0x0a0b028de6cf6e8446853a300061305501136cefa5f5eb3e96afd95dbd73dd92":{"error":"revert","program_counter":609,"return":"0x"}},"hashes":["0x0a0b028de6cf6e8446853a300061305501136cefa5f5eb3e96afd95dbd73dd92"],"message":"VM Exception while processing transaction: revert"}, tx={"data":"0xdbdff2c1","to":{},"from":"0x17ec8597ff92C3F44523bDc65BF0f1bE632917ff","gasPrice":{"type":"BigNumber","hex":"0x77359400"},"type":0,"nonce":{},"gasLimit":{},"chainId":{}}, code=UNPREDICTABLE_GAS_LIMIT, version=abstract-signer/5.4.1)
156 at Logger.makeError (node_modules/@ethersproject/logger/src.ts/index.ts:225:28)
157 at Logger.throwError (node_modules/@ethersproject/logger/src.ts/index.ts:237:20)
158 at /home/name/git/trucol/tested/new_test/test_vrf3/node_modules/@ethersproject/abstract-signer/src.ts/index.ts:301:31
159 at process._tickCallback (internal/process/next_tick.js:68:7)
160
161
162
163 3 passing (4s)
164
Which set of files, file structure and commands do I need to automatically test whether the getRandomNumber()
contract returns an integer if sufficient "gas" is provided, and an error otherwise?
ANSWER
Answered 2021-Sep-09 at 04:35to test locally you need to make use of mocks which can simulate having an oracle network. Because you're working locally, a Chainlink node doesn't know about your local blockchain, so you can't actually do proper VRF requests. Note you can try deploy a local Chainlink node and a local blockchain and have them talk, but it isn't fully supported yet so you may get mixed results. Anyway, as per the hardhat starter kit that you linked, you can set the defaultNetwork to be 'hardhat' in the hardhat.config.js file, then when you deploy and run the integration tests (yarn test-integration), it will use mocks to mock up the VRF node, and to test the requesting of a random number. See the test here, and the mock contracts and linktoken get deployed here
QUESTION
How do I give my JSON schema an absolute URL for its $id when I haven't published it yet because it hasn't been tested yet?
Asked 2022-Mar-07 at 00:41I'm putting together JSON schemas and I'd like to use $ref to DRY my schemas. I'll have many schemas that will each use common subschemas. I want to unit test my schemas before publishing them by writing unit tests that assert that, given certain input, the input is deemed valid or invalid, using a JSON schema library that I trust to be correct (so that I'm just testing my schemas, not the library).
Where I get confused is that in order to load my schemas before I've published them (which I want to do while running tests locally and during CI/CD), I need to use relative local paths like this:
1"pet": { "$ref": "file://./schemas/components/pet.schema.json" }
2
That's because that pet schema hasn't been published to a URL yet. It hasn't been verified by automated tests that it's correct yet. This works well enough for running tests, and it also worked well for packaging inside a Docker image so that the schemas could be loaded from disk as the app starts up.
But then, if I were to give one of the top level schemas to someone (that leverages $ref) after publishing it to an absolute URL, it wouldn't load in their program because of that path that I used that worked only for my unit testing.
I found that I had to publish my schemas using absolute URLs in order for them to be used in consuming programs. I ended up publishing the schemas https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/person.1-0-0.schema.json and https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/components/pet.1-0-0.schema.json that way. I tested that they worked fine in a consuming program by writing the program:
1"pet": { "$ref": "file://./schemas/components/pet.schema.json" }
2package main
3
4import (
5 "fmt"
6
7 "github.com/xeipuuv/gojsonschema"
8)
9
10func main() {
11 schemaLoader := gojsonschema.NewReferenceLoader("https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/person.1-0-0.schema.json")
12
13 jsonStr := `
14 {
15 "name": "Matt",
16 "pet": {
17 "name": "Shady"
18 }
19 }
20 `
21
22 documentLoader := gojsonschema.NewStringLoader(jsonStr)
23
24 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
25 if err != nil {
26 panic(fmt.Errorf("could not validate: %w", err))
27 }
28
29 if result.Valid() {
30 fmt.Printf("The document is valid.\n")
31 } else {
32 fmt.Printf("The document is not valid. See errors:\n")
33 for _, desc := range result.Errors() {
34 fmt.Printf("- %s\n", desc)
35 }
36 }
37}
38
Which resulted in the following expected output:
1"pet": { "$ref": "file://./schemas/components/pet.schema.json" }
2package main
3
4import (
5 "fmt"
6
7 "github.com/xeipuuv/gojsonschema"
8)
9
10func main() {
11 schemaLoader := gojsonschema.NewReferenceLoader("https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/person.1-0-0.schema.json")
12
13 jsonStr := `
14 {
15 "name": "Matt",
16 "pet": {
17 "name": "Shady"
18 }
19 }
20 `
21
22 documentLoader := gojsonschema.NewStringLoader(jsonStr)
23
24 result, err := gojsonschema.Validate(schemaLoader, documentLoader)
25 if err != nil {
26 panic(fmt.Errorf("could not validate: %w", err))
27 }
28
29 if result.Valid() {
30 fmt.Printf("The document is valid.\n")
31 } else {
32 fmt.Printf("The document is not valid. See errors:\n")
33 for _, desc := range result.Errors() {
34 fmt.Printf("- %s\n", desc)
35 }
36 }
37}
38The document is valid.
39
So I'm confused about this "chicken and egg" situation.
I was able to publish schemas that could be used, as long as I didn't unit test them before publishing them.
And I was able to unit test schemas as long as:
- I didn't want to publish them in the form that was verified by the unit testing to be correct.
- I'm okay with my application loading them via HTTPS as it starts up instead of loading them from disk. I'm worried about this because I don't want a web server to be a point of failure for my app starting up.
I would appreciate some insight in how one might accomplish both goals.
ANSWER
Answered 2022-Mar-07 at 00:41Where I get confused is that in order to load my schemas before I've published them (which I want to do while running tests locally and during CI/CD), I need to use relative local paths
Your initial assumption is false. URIs used in the $id
keyword can be arbitrary identifiers -- they do not need to be resolvable via the network or disk at the stated location. In fact, it is an error for a JSON Schema implementation to assume to find schema documents at the stated location: they MUST support being able to load documents locally and associate them with the stated identifier:
The "$id" keyword identifies a schema resource with its canonical URI.
Note that this URI is an identifier and not necessarily a network locator. In the case of a network-addressable URL, a schema need not be downloadable from its canonical URI.
A schema need not be downloadable from the address if it is a network-addressable URL, and implementations SHOULD NOT assume they should perform a network operation when they encounter a network-addressable URI.
Therefore, you can give your schema document any identifier you like, such as the URI you anticipate using when you eventually publish your schema for public consumption, and perform local testing using that identifier.
Any implementation that does not support doing this is in violation of the specification, and this should be reported to its maintainers as a bug.
QUESTION
How to mock function without class in Kotlin?
Asked 2022-Feb-27 at 16:311fun add() {
2 return 4+1;
3}
4
5
6class Calculator {
7 fun MathUtils() {
8 // do something
9 // calls add() function
10 val x: Int = add()
11
12 // return something
13 return x + 22
14 }
15}
16
17
18class CalculatorTest {
19 var c = Calculator()
20
21 @Test
22 fun MathUtilsSuccess() {
23 Assertions.assertThat(
24 c.MathUtils()
25 ).isEqualTo(24)
26 }
27}
28
I am new to unit Testing, I want to know is there any way I can call MathUtils()
function (inside Calculator class)
in MathUtilsSuccess()
(inside CalculatorTest class) and i have to mock add()
function which is not inside any class such that add()
always returns 2, so that in success scenario my Test pass.
All the classes are in separate file and fun add() is also in separate file.
P.S : I have broken down my doubt into this simple example, This is not the actual problem i am working on.
ANSWER
Answered 2022-Feb-27 at 12:50Indeed io.mockk:mockk supports to mock top-level functions in Kotlin.
Similar to extension functions, for top-level functions, Kotlin creates a class containing static functions under the hood as well, which in turn can be mocked.
However, you need to know the name of the underlying class created, which hints that probably this approach should only be used very rarely and with caution. I'll first demonstrate a working sample and name some alternative approaches afterwards.
Let's look at an example on how to mock a top-level function with MockK
.
foo.kt
1fun add() {
2 return 4+1;
3}
4
5
6class Calculator {
7 fun MathUtils() {
8 // do something
9 // calls add() function
10 val x: Int = add()
11
12 // return something
13 return x + 22
14 }
15}
16
17
18class CalculatorTest {
19 var c = Calculator()
20
21 @Test
22 fun MathUtilsSuccess() {
23 Assertions.assertThat(
24 c.MathUtils()
25 ).isEqualTo(24)
26 }
27}
28package tld.domain.example
29
30fun foo(x: Int): Int = x + 3
31
bar.kt
1fun add() {
2 return 4+1;
3}
4
5
6class Calculator {
7 fun MathUtils() {
8 // do something
9 // calls add() function
10 val x: Int = add()
11
12 // return something
13 return x + 22
14 }
15}
16
17
18class CalculatorTest {
19 var c = Calculator()
20
21 @Test
22 fun MathUtilsSuccess() {
23 Assertions.assertThat(
24 c.MathUtils()
25 ).isEqualTo(24)
26 }
27}
28package tld.domain.example
29
30fun foo(x: Int): Int = x + 3
31package tld.domain.example
32
33fun bar(z: Int): Int = foo(z) + 2
34
You can mock static "things", such as extension functions, top-level functions and objects using mockkStatic
.
1fun add() {
2 return 4+1;
3}
4
5
6class Calculator {
7 fun MathUtils() {
8 // do something
9 // calls add() function
10 val x: Int = add()
11
12 // return something
13 return x + 22
14 }
15}
16
17
18class CalculatorTest {
19 var c = Calculator()
20
21 @Test
22 fun MathUtilsSuccess() {
23 Assertions.assertThat(
24 c.MathUtils()
25 ).isEqualTo(24)
26 }
27}
28package tld.domain.example
29
30fun foo(x: Int): Int = x + 3
31package tld.domain.example
32
33fun bar(z: Int): Int = foo(z) + 2
34internal class BarKtTest {
35
36 @Test
37 internal fun `can work without mock`() {
38 unmockkAll() // just to show nothing is mocked anymore
39
40 val result = bar(1)
41
42 assertThat(result, equalTo(6))
43 }
44
45 @Test
46 internal fun `can be mocked`() {
47 mockkStatic("tld.domain.example.FooKt")
48 every { foo(any()) } returns 1
49
50 val result = bar(1)
51
52 assertThat(result, equalTo(3))
53 }
54}
55
As seen above, one is able to mock the top-level function yielding different results. The example above use com.natpryce:hamkrest for their assertions, but that should not matter.
When using IntelliJ, you can retrieve the name of the underlying class by using Tools > Kotlin > Show Kotlin Bytecode
. In my example above, this yields a
1fun add() {
2 return 4+1;
3}
4
5
6class Calculator {
7 fun MathUtils() {
8 // do something
9 // calls add() function
10 val x: Int = add()
11
12 // return something
13 return x + 22
14 }
15}
16
17
18class CalculatorTest {
19 var c = Calculator()
20
21 @Test
22 fun MathUtilsSuccess() {
23 Assertions.assertThat(
24 c.MathUtils()
25 ).isEqualTo(24)
26 }
27}
28package tld.domain.example
29
30fun foo(x: Int): Int = x + 3
31package tld.domain.example
32
33fun bar(z: Int): Int = foo(z) + 2
34internal class BarKtTest {
35
36 @Test
37 internal fun `can work without mock`() {
38 unmockkAll() // just to show nothing is mocked anymore
39
40 val result = bar(1)
41
42 assertThat(result, equalTo(6))
43 }
44
45 @Test
46 internal fun `can be mocked`() {
47 mockkStatic("tld.domain.example.FooKt")
48 every { foo(any()) } returns 1
49
50 val result = bar(1)
51
52 assertThat(result, equalTo(3))
53 }
54}
55public final class tld/domain/example/FooKt {
56 ...
57
There is an overloaded version of mockkStatic
that allows to provide a function reference instead of hard-coding the package name and class name as String. Note however, that this relies on the same methodology under the hood.
1fun add() {
2 return 4+1;
3}
4
5
6class Calculator {
7 fun MathUtils() {
8 // do something
9 // calls add() function
10 val x: Int = add()
11
12 // return something
13 return x + 22
14 }
15}
16
17
18class CalculatorTest {
19 var c = Calculator()
20
21 @Test
22 fun MathUtilsSuccess() {
23 Assertions.assertThat(
24 c.MathUtils()
25 ).isEqualTo(24)
26 }
27}
28package tld.domain.example
29
30fun foo(x: Int): Int = x + 3
31package tld.domain.example
32
33fun bar(z: Int): Int = foo(z) + 2
34internal class BarKtTest {
35
36 @Test
37 internal fun `can work without mock`() {
38 unmockkAll() // just to show nothing is mocked anymore
39
40 val result = bar(1)
41
42 assertThat(result, equalTo(6))
43 }
44
45 @Test
46 internal fun `can be mocked`() {
47 mockkStatic("tld.domain.example.FooKt")
48 every { foo(any()) } returns 1
49
50 val result = bar(1)
51
52 assertThat(result, equalTo(3))
53 }
54}
55public final class tld/domain/example/FooKt {
56 ...
57mockkStatic(::foo)
58
Instead of using static mocks, you could also make use of the dependency inversion principle, that is you inject the implementation of foo
into bar
somehow, e.g. through it's parameters or by wrapping it in a class containing a fields or using a higher level function.
1fun add() {
2 return 4+1;
3}
4
5
6class Calculator {
7 fun MathUtils() {
8 // do something
9 // calls add() function
10 val x: Int = add()
11
12 // return something
13 return x + 22
14 }
15}
16
17
18class CalculatorTest {
19 var c = Calculator()
20
21 @Test
22 fun MathUtilsSuccess() {
23 Assertions.assertThat(
24 c.MathUtils()
25 ).isEqualTo(24)
26 }
27}
28package tld.domain.example
29
30fun foo(x: Int): Int = x + 3
31package tld.domain.example
32
33fun bar(z: Int): Int = foo(z) + 2
34internal class BarKtTest {
35
36 @Test
37 internal fun `can work without mock`() {
38 unmockkAll() // just to show nothing is mocked anymore
39
40 val result = bar(1)
41
42 assertThat(result, equalTo(6))
43 }
44
45 @Test
46 internal fun `can be mocked`() {
47 mockkStatic("tld.domain.example.FooKt")
48 every { foo(any()) } returns 1
49
50 val result = bar(1)
51
52 assertThat(result, equalTo(3))
53 }
54}
55public final class tld/domain/example/FooKt {
56 ...
57mockkStatic(::foo)
58fun barWithParam(foo: (Int) -> Int, z: Int): Int =
59 foo(z) + 2
60
61class BarProvider(private val foo: (Int) -> Int) {
62 fun bar(z: Int): Int = foo(z) + 2
63}
64
65fun barFactory(foo: (Int) -> Int): (Int) -> Int {
66 return { z -> foo(z) + 2 }
67}
68val bar = barFactory(::foo)
69
Another approach is to simply ignore the fact, that bar
uses foo
under the hood and tests the behaviour of bar
without mocking foo
. This mostly works, when foo
is a pure function that does not have any side-effect, such as making any I/O operations, e.g. network, disk ...
QUESTION
What is the 'X' in this insert > X'3C2F756C3E'
Asked 2022-Feb-25 at 14:37We have something similar to the following in the SQL that builds a test database for use with phpunit testing:
1INSERT (101, 23, 'test1', 'something', 0x3C2F756C3E)
2INSERT INTO `tableA` (`field1`, `field2`, `field3`, `field4`, `field5`)
3VALUES
4 (102, 23, 'test2', 'something', X'3C2F756C3E');
5
To my surprise this actually runs when I run it as a query and it inserts </ul>
into field5.
I have tried searching for some info on the 'X' part but haven't found anything, I can see that there is something related to HTML entities here, but what is the 'X"? How does this work?
ANSWER
Answered 2022-Feb-25 at 14:37The X
signifies that the following is a hex-encoded binary string literal. It is defined in the ISO-9075-2:2016 SQL Standard as <binary string literal>:
1INSERT (101, 23, 'test1', 'something', 0x3C2F756C3E)
2INSERT INTO `tableA` (`field1`, `field2`, `field3`, `field4`, `field5`)
3VALUES
4 (102, 23, 'test2', 'something', X'3C2F756C3E');
5<binary string literal> ::=
6 X <quote> [ <space>... ] [ { <hexit> [ <space>... ] <hexit> [ <space>... ] }... ] <quote>
7 [ { <separator> <quote> [ <space>... ] [ { <hexit> [ <space>... ]
8 <hexit> [ <space>... ] }... ] <quote> }... ]
9
10<hexit> ::=
11 <digit> | A | B | C | D | E | F | a | b | c | d | e | f
12
The literal X'3C2F756C3E'
when interpreted in ASCII or UTF-8 is:
- 3C ->
<
- 2F ->
/
- 75 ->
u
- 6C ->
l
- 3E ->
>
or </ul>
.
See also the MySQL documentation, section Hexadecimal Literals (note: the MySQL syntax is more restrictive than the standard syntax, that is, MySQL does not allow spaces between hexits, nor splitting into multiple quote delimited sections).
QUESTION
Deploying Uniswap v2 / Sushiswap or similar in Brownie, Hardhat or Truffle test suite
Asked 2022-Feb-20 at 10:59I am writing an automated test suite that needs to test functions against Uniswap v2 style automated market marker: do swaps and use different order routing. Thus, routers need to be deployed.
Are there any existing examples of how to deploy a testable Uniswap v2 style exchange in Brownie? Because Brownie is a minority of smart contract developers, are there any examples for Truffle or Hardhat?
I am also exploring the option of using a mainnet fork, but I am not sure if this operation is too expensive (slow) to be used in unit testing.
ANSWER
Answered 2022-Feb-10 at 14:30Using a local testnet allows you to control very precisely the state of the blockchain during your test. However, it will require you to deploy every contract you need manually.
A fork of the mainnet will save you from having to deploy every contract already deployed on the mainnet. However you will sacrifice control over the environment and will require a connection to a node.
I've deployed Uniswap 2V on a testnet a few times. To do it you will need the bytecode and ABI for the following contracts: UniswapV2Factory, UniswapV2Pair, UniswapV2Router02 (I suppose you want the second version of the router). The Uniswap docs explains very well how to download them from NPM. For the router to work properly you will also need to deploy a WETH contract. I suggest deploying the one from this github page.
Before running this code, just make sure that your chain is running. For hardhat run the following command:
1npx hardhat node
2
Start by connecting your signer to your dev chain:
1npx hardhat node
2var provider = new ethers.providers.WebSocketProvider("ws://localhost:8545");
3var signer = provider.getSigner();
4
Using the ethers.js library, you first deploy the factory:
1npx hardhat node
2var provider = new ethers.providers.WebSocketProvider("ws://localhost:8545");
3var signer = provider.getSigner();
4const compiledUniswapFactory = require("@uniswap/v2-core/build/UniswapV2Factory.json");
5var uniswapFactory = await new ethers.ContractFactory(compiledUniswapFactory.interface,compiledUniswapFactory.bytecode,signer).deploy(await signer.getAddress());
6
Then the WETH contract:
1npx hardhat node
2var provider = new ethers.providers.WebSocketProvider("ws://localhost:8545");
3var signer = provider.getSigner();
4const compiledUniswapFactory = require("@uniswap/v2-core/build/UniswapV2Factory.json");
5var uniswapFactory = await new ethers.ContractFactory(compiledUniswapFactory.interface,compiledUniswapFactory.bytecode,signer).deploy(await signer.getAddress());
6const compiledWETH = require("canonical-weth/build/conrtacts/WETH.json";
7var WETH = await new ethers.ContractFactory(WETH.interface,WETH.bytecode,signer).deploy();
8
You can now deploy the router.
1npx hardhat node
2var provider = new ethers.providers.WebSocketProvider("ws://localhost:8545");
3var signer = provider.getSigner();
4const compiledUniswapFactory = require("@uniswap/v2-core/build/UniswapV2Factory.json");
5var uniswapFactory = await new ethers.ContractFactory(compiledUniswapFactory.interface,compiledUniswapFactory.bytecode,signer).deploy(await signer.getAddress());
6const compiledWETH = require("canonical-weth/build/conrtacts/WETH.json";
7var WETH = await new ethers.ContractFactory(WETH.interface,WETH.bytecode,signer).deploy();
8const compiledUniswapRouter = require("@uniswap/v2-periphery/build/UniswapV2Router02");
9var router = await new ethers.ContractFactory(compiledUniswapRouter.abi,compiledUniswapRouter.bytecode,signer).deploy(uniswapFactory.address,WETH.address);
10
You will also need to deploy the ERC20 tokens you need (Here is an example with tokens I've written):
1npx hardhat node
2var provider = new ethers.providers.WebSocketProvider("ws://localhost:8545");
3var signer = provider.getSigner();
4const compiledUniswapFactory = require("@uniswap/v2-core/build/UniswapV2Factory.json");
5var uniswapFactory = await new ethers.ContractFactory(compiledUniswapFactory.interface,compiledUniswapFactory.bytecode,signer).deploy(await signer.getAddress());
6const compiledWETH = require("canonical-weth/build/conrtacts/WETH.json";
7var WETH = await new ethers.ContractFactory(WETH.interface,WETH.bytecode,signer).deploy();
8const compiledUniswapRouter = require("@uniswap/v2-periphery/build/UniswapV2Router02");
9var router = await new ethers.ContractFactory(compiledUniswapRouter.abi,compiledUniswapRouter.bytecode,signer).deploy(uniswapFactory.address,WETH.address);
10const compiledERC20 = require("../../../Ethereum/Ethereum/sources/ERC20.sol/Token.json");
11var erc20Factory = new ethers.ContractFactory(compiledERC20.abi,compiledERC20.bytecode,signer);
12
13var erc20_0 = await erc20Factory.deploy("1000000", "Token 0", "5", "T0");
14var erc20_1 = await erc20Factory.deploy("1000000", "Token 1", "5", "T1");
15
The parameters of the deploy function will depend on the constructor of the token you wish to deploy.
You will also want to create pairs using the createPair method of the Uniswap factory.
1npx hardhat node
2var provider = new ethers.providers.WebSocketProvider("ws://localhost:8545");
3var signer = provider.getSigner();
4const compiledUniswapFactory = require("@uniswap/v2-core/build/UniswapV2Factory.json");
5var uniswapFactory = await new ethers.ContractFactory(compiledUniswapFactory.interface,compiledUniswapFactory.bytecode,signer).deploy(await signer.getAddress());
6const compiledWETH = require("canonical-weth/build/conrtacts/WETH.json";
7var WETH = await new ethers.ContractFactory(WETH.interface,WETH.bytecode,signer).deploy();
8const compiledUniswapRouter = require("@uniswap/v2-periphery/build/UniswapV2Router02");
9var router = await new ethers.ContractFactory(compiledUniswapRouter.abi,compiledUniswapRouter.bytecode,signer).deploy(uniswapFactory.address,WETH.address);
10const compiledERC20 = require("../../../Ethereum/Ethereum/sources/ERC20.sol/Token.json");
11var erc20Factory = new ethers.ContractFactory(compiledERC20.abi,compiledERC20.bytecode,signer);
12
13var erc20_0 = await erc20Factory.deploy("1000000", "Token 0", "5", "T0");
14var erc20_1 = await erc20Factory.deploy("1000000", "Token 1", "5", "T1");
15uniswapFactory.createPair(erc20_0.address,erc20_1.address);
16
Keep in mind that in the pair the tokens will be ordered arbitrarly by the contract. ERC20_0 might not be the first of the two.
After that just wait for all the transactions to go through and you should be good to start your test.
QUESTION
How to add unit testing in strapi version 4 using jest?
Asked 2022-Jan-25 at 19:26I'm trying to add unit testing on a brand new strapi application. The official documentation is still in progress. So, until the documentation is being ready is there a way to add jest unit testing to strapi application ? i followed the approach in the v3 documentation with no luck.
ANSWER
Answered 2022-Jan-25 at 19:26There are quite a few changes from Strapi V3 to Strapi V4 when it comes to initializing a Strapi application's APIs. The most important changes are how Strapi populates the KOA routes, and how to make requests to the http server.
To populate the KOA routes use
1await instance.server.mount();
2
instead of
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6
To call the http server use
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7
instead of
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8
You also need to use the new database configuration schema when defining your test database. You can use the following as an initial setup for your tests.
The following is an updated (and WIP) guide based on Strapi V3 Unit Testing guide.First run
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8 yarn add --dev jest supertest sqlite3
9
or
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8 yarn add --dev jest supertest sqlite3
9 npm install --save-dev jest supertest sqlite3
10
Then add the following to your ./package.json scripts:
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8 yarn add --dev jest supertest sqlite3
9 npm install --save-dev jest supertest sqlite3
10"scripts": {
11 // ...strapi scripts
12 "test": "jest --forceExit --detectOpenHandles", //add
13 "watch": "yarn test --watch", // optional
14}
15
Then add the following files:
./jest.config.js
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8 yarn add --dev jest supertest sqlite3
9 npm install --save-dev jest supertest sqlite3
10"scripts": {
11 // ...strapi scripts
12 "test": "jest --forceExit --detectOpenHandles", //add
13 "watch": "yarn test --watch", // optional
14}
15module.exports = {
16 verbose: true,
17 testPathIgnorePatterns: [
18 "/node_modules/",
19 ".tmp",
20 ".cache"
21 ],
22 modulePaths: [
23 "/node_modules/",
24 ],
25 testEnvironment: "node",
26};
27
./config/env/test/database.json
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8 yarn add --dev jest supertest sqlite3
9 npm install --save-dev jest supertest sqlite3
10"scripts": {
11 // ...strapi scripts
12 "test": "jest --forceExit --detectOpenHandles", //add
13 "watch": "yarn test --watch", // optional
14}
15module.exports = {
16 verbose: true,
17 testPathIgnorePatterns: [
18 "/node_modules/",
19 ".tmp",
20 ".cache"
21 ],
22 modulePaths: [
23 "/node_modules/",
24 ],
25 testEnvironment: "node",
26};
27{
28 "connection": {
29 "client": "sqlite",
30 "connection": {
31 "filename": ".tmp/test.db"
32 },
33 "useNullAsDefault": true,
34 "pool": {
35 "min": 0,
36 "max": 1
37 }
38 }
39}
40
./tests/helpers/strapi.js
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8 yarn add --dev jest supertest sqlite3
9 npm install --save-dev jest supertest sqlite3
10"scripts": {
11 // ...strapi scripts
12 "test": "jest --forceExit --detectOpenHandles", //add
13 "watch": "yarn test --watch", // optional
14}
15module.exports = {
16 verbose: true,
17 testPathIgnorePatterns: [
18 "/node_modules/",
19 ".tmp",
20 ".cache"
21 ],
22 modulePaths: [
23 "/node_modules/",
24 ],
25 testEnvironment: "node",
26};
27{
28 "connection": {
29 "client": "sqlite",
30 "connection": {
31 "filename": ".tmp/test.db"
32 },
33 "useNullAsDefault": true,
34 "pool": {
35 "min": 0,
36 "max": 1
37 }
38 }
39}
40const Strapi = require("@strapi/strapi");
41const fs = require("fs");
42
43let instance;
44
45async function setupStrapi() {
46 if (!instance) {
47 await Strapi().load();
48 instance = strapi;
49 await instance.server.mount();
50 }
51 return instance;
52}
53
54async function cleanupStrapi() {
55 const dbSettings = strapi.config.get("database.connection");
56 const tmpDbFile = dbSettings.connection.filename
57
58 //close server to release the db-file
59 await strapi.server.httpServer.close();
60
61 //delete test database after all tests
62 if (dbSettings && tmpDbFile) {
63 if (fs.existsSync(tmpDbFile)) {
64 fs.unlinkSync(tmpDbFile);
65 }
66 }
67 // close the connection to the database
68 await strapi.db.connection.destroy();
69}
70
71module.exports = { setupStrapi, cleanupStrapi };
72
Note that you need to have the /hello endpoint in your project as specified in the strapi docs for the next tests to pass.
./tests/app.test.js
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8 yarn add --dev jest supertest sqlite3
9 npm install --save-dev jest supertest sqlite3
10"scripts": {
11 // ...strapi scripts
12 "test": "jest --forceExit --detectOpenHandles", //add
13 "watch": "yarn test --watch", // optional
14}
15module.exports = {
16 verbose: true,
17 testPathIgnorePatterns: [
18 "/node_modules/",
19 ".tmp",
20 ".cache"
21 ],
22 modulePaths: [
23 "/node_modules/",
24 ],
25 testEnvironment: "node",
26};
27{
28 "connection": {
29 "client": "sqlite",
30 "connection": {
31 "filename": ".tmp/test.db"
32 },
33 "useNullAsDefault": true,
34 "pool": {
35 "min": 0,
36 "max": 1
37 }
38 }
39}
40const Strapi = require("@strapi/strapi");
41const fs = require("fs");
42
43let instance;
44
45async function setupStrapi() {
46 if (!instance) {
47 await Strapi().load();
48 instance = strapi;
49 await instance.server.mount();
50 }
51 return instance;
52}
53
54async function cleanupStrapi() {
55 const dbSettings = strapi.config.get("database.connection");
56 const tmpDbFile = dbSettings.connection.filename
57
58 //close server to release the db-file
59 await strapi.server.httpServer.close();
60
61 //delete test database after all tests
62 if (dbSettings && tmpDbFile) {
63 if (fs.existsSync(tmpDbFile)) {
64 fs.unlinkSync(tmpDbFile);
65 }
66 }
67 // close the connection to the database
68 await strapi.db.connection.destroy();
69}
70
71module.exports = { setupStrapi, cleanupStrapi };
72const { setupStrapi, cleanupStrapi } = require("./helpers/strapi");
73
74jest.setTimeout(15000);
75
76beforeAll(async () => {
77 await setupStrapi();
78});
79
80afterAll(async () => {
81 await cleanupStrapi();
82});
83
84it("strapi is defined", () => {
85 expect(strapi).toBeDefined();
86});
87
88require('./hello')
89
./tests/hello/index.js
1await instance.server.mount();
2await instance.app
3 .use(instance.router.routes()) // populate KOA routes
4 .use(instance.router.allowedMethods()); // populate KOA methods
5instance.server = http.createServer(instance.app.callback());
6 strapi.server.httpServer
7 strapi.server
8 yarn add --dev jest supertest sqlite3
9 npm install --save-dev jest supertest sqlite3
10"scripts": {
11 // ...strapi scripts
12 "test": "jest --forceExit --detectOpenHandles", //add
13 "watch": "yarn test --watch", // optional
14}
15module.exports = {
16 verbose: true,
17 testPathIgnorePatterns: [
18 "/node_modules/",
19 ".tmp",
20 ".cache"
21 ],
22 modulePaths: [
23 "/node_modules/",
24 ],
25 testEnvironment: "node",
26};
27{
28 "connection": {
29 "client": "sqlite",
30 "connection": {
31 "filename": ".tmp/test.db"
32 },
33 "useNullAsDefault": true,
34 "pool": {
35 "min": 0,
36 "max": 1
37 }
38 }
39}
40const Strapi = require("@strapi/strapi");
41const fs = require("fs");
42
43let instance;
44
45async function setupStrapi() {
46 if (!instance) {
47 await Strapi().load();
48 instance = strapi;
49 await instance.server.mount();
50 }
51 return instance;
52}
53
54async function cleanupStrapi() {
55 const dbSettings = strapi.config.get("database.connection");
56 const tmpDbFile = dbSettings.connection.filename
57
58 //close server to release the db-file
59 await strapi.server.httpServer.close();
60
61 //delete test database after all tests
62 if (dbSettings && tmpDbFile) {
63 if (fs.existsSync(tmpDbFile)) {
64 fs.unlinkSync(tmpDbFile);
65 }
66 }
67 // close the connection to the database
68 await strapi.db.connection.destroy();
69}
70
71module.exports = { setupStrapi, cleanupStrapi };
72const { setupStrapi, cleanupStrapi } = require("./helpers/strapi");
73
74jest.setTimeout(15000);
75
76beforeAll(async () => {
77 await setupStrapi();
78});
79
80afterAll(async () => {
81 await cleanupStrapi();
82});
83
84it("strapi is defined", () => {
85 expect(strapi).toBeDefined();
86});
87
88require('./hello')
89const request = require('supertest');
90
91it('should return hello world', async () => {
92 await request(strapi.server.httpServer)
93 .get('/api/hello')
94 .expect(200) // Expect response http code 200
95});
96
I hope this helps anyone struggling with the same issues. I will update the answer as I progress.
QUESTION
What exactly is/means "System.IO.Stream+NullStream"?
Asked 2022-Jan-24 at 13:44I am currently experimenting to find the proper way of creating an HTTP context for unit testing purposes. While examining the stream types created, I was surprised that the following returns a type of System.IO.Stream+NullStream
.
1var context = new DefaultHttpContext();
2
3var requestBodyType = context.Request.Body.GetType().FullName;
4var responseBodyType = context.Response.Body.GetType().FullName;
5
If the test case requires it, I'd simply replace the stream with a MemoryStream
and then act upon it accordingly.
1var context = new DefaultHttpContext();
2
3var requestBodyType = context.Request.Body.GetType().FullName;
4var responseBodyType = context.Response.Body.GetType().FullName;
5var memoryStream = new MemoryStream();
6var context = new DefaultHttpContext();
7context.Response.Body = memoryStream;
8
So far so good. But what exactly is System.IO.Stream+NullStream
? I would have expected to find a type of NullStream
in the documentation, but couldn't find such a thing.
ANSWER
Answered 2022-Jan-24 at 13:33It's an internal type.
It sub-classes Stream
, and is exposed as Stream.Null
.
As you can see from the implementation, it just throws away any data written to it, and doesn't return any data if asked.
QUESTION
Unit testing SwiftUI/Combine @Published boolean values
Asked 2022-Jan-23 at 00:46I am trying to acquaint myself with unit testing some view models in SwiftUI. The view model currently has two @Published
boolean values that publish changes when an underlying UserDefaults
property changes. For my unit tests, I have followed this guide on how to setup UserDefaults
for testing so my production values are not modified. I am able to test the default value as such:
1func testDefaultValue() {
2 XCTAssertFalse(viewModel.canDoThing)
3}
4
How would I go about toggling the @Published
value then ensuring my view model has received the changes? So for instance, I have a reference to my mock user defaults in my XCTestCase. I attempted to do the following with zero success:
1func testDefaultValue() {
2 XCTAssertFalse(viewModel.canDoThing)
3}
4func testValueTogglesToTrue() {
5 defaults.canDoThing = true
6 XCTAssertTrue(viewModel.canDoThing)
7
}
The thought being that updating the underlying user defaults value that is publishing changes to the published value in the view model will notify our view model. The above does not do anything to the view model variable. Do I need to subscribe to the publisher and use sink to accomplish this?
ANSWER
Answered 2022-Jan-22 at 16:07Let's say you store a flag in UserDefaults
to know whether the user has completed onboarding:
1func testDefaultValue() {
2 XCTAssertFalse(viewModel.canDoThing)
3}
4func testValueTogglesToTrue() {
5 defaults.canDoThing = true
6 XCTAssertTrue(viewModel.canDoThing)
7extension UserDefaults {
8
9 @objc dynamic public var completedOnboarding: Bool {
10 bool(forKey: "completedOnboarding")
11 }
12}
13
You have a ViewModel which tells your View
whether to show onboarding or not and has a method to mark onboarding as completed:
1func testDefaultValue() {
2 XCTAssertFalse(viewModel.canDoThing)
3}
4func testValueTogglesToTrue() {
5 defaults.canDoThing = true
6 XCTAssertTrue(viewModel.canDoThing)
7extension UserDefaults {
8
9 @objc dynamic public var completedOnboarding: Bool {
10 bool(forKey: "completedOnboarding")
11 }
12}
13class ViewModel: ObservableObject {
14
15 @Published private(set) var showOnboarding: Bool = true
16
17 private let userDefaults: UserDefaults
18
19 public init(userDefaults: UserDefaults) {
20 self.userDefaults = userDefaults
21 self.showOnboarding = !userDefaults.completedOnboarding
22 userDefaults
23 .publisher(for: \.completedOnboarding)
24 .map { !$0 }
25 .receive(on: RunLoop.main)
26 .assign(to: &$showOnboarding)
27 }
28
29 public func completedOnboarding() {
30 userDefaults.set(true, forKey: "completedOnboarding")
31 }
32}
33
To test this class you have a XCTestCase
:
1func testDefaultValue() {
2 XCTAssertFalse(viewModel.canDoThing)
3}
4func testValueTogglesToTrue() {
5 defaults.canDoThing = true
6 XCTAssertTrue(viewModel.canDoThing)
7extension UserDefaults {
8
9 @objc dynamic public var completedOnboarding: Bool {
10 bool(forKey: "completedOnboarding")
11 }
12}
13class ViewModel: ObservableObject {
14
15 @Published private(set) var showOnboarding: Bool = true
16
17 private let userDefaults: UserDefaults
18
19 public init(userDefaults: UserDefaults) {
20 self.userDefaults = userDefaults
21 self.showOnboarding = !userDefaults.completedOnboarding
22 userDefaults
23 .publisher(for: \.completedOnboarding)
24 .map { !$0 }
25 .receive(on: RunLoop.main)
26 .assign(to: &$showOnboarding)
27 }
28
29 public func completedOnboarding() {
30 userDefaults.set(true, forKey: "completedOnboarding")
31 }
32}
33class MyTestCase: XCTestCase {
34
35 private var userDefaults: UserDefaults!
36 private var cancellables = Set<AnyCancellable>()
37
38 override func setUpWithError() throws {
39 try super.setUpWithError()
40 userDefaults = try XCTUnwrap(UserDefaults(suiteName: #file))
41 userDefaults.removePersistentDomain(forName: #file)
42 }
43
44 // ...
45}
46
Some of the test cases are synchronous for example you can easily test that showOnboarding depends on UserDefaults
completedOnboarding property:
1func testDefaultValue() {
2 XCTAssertFalse(viewModel.canDoThing)
3}
4func testValueTogglesToTrue() {
5 defaults.canDoThing = true
6 XCTAssertTrue(viewModel.canDoThing)
7extension UserDefaults {
8
9 @objc dynamic public var completedOnboarding: Bool {
10 bool(forKey: "completedOnboarding")
11 }
12}
13class ViewModel: ObservableObject {
14
15 @Published private(set) var showOnboarding: Bool = true
16
17 private let userDefaults: UserDefaults
18
19 public init(userDefaults: UserDefaults) {
20 self.userDefaults = userDefaults
21 self.showOnboarding = !userDefaults.completedOnboarding
22 userDefaults
23 .publisher(for: \.completedOnboarding)
24 .map { !$0 }
25 .receive(on: RunLoop.main)
26 .assign(to: &$showOnboarding)
27 }
28
29 public func completedOnboarding() {
30 userDefaults.set(true, forKey: "completedOnboarding")
31 }
32}
33class MyTestCase: XCTestCase {
34
35 private var userDefaults: UserDefaults!
36 private var cancellables = Set<AnyCancellable>()
37
38 override func setUpWithError() throws {
39 try super.setUpWithError()
40 userDefaults = try XCTUnwrap(UserDefaults(suiteName: #file))
41 userDefaults.removePersistentDomain(forName: #file)
42 }
43
44 // ...
45}
46func test_whenCompletedOnboardingFalse_thenShowOnboardingTrue() {
47 userDefaults.set(false, forKey: "completedOnboarding")
48 let subject = ViewModel(userDefaults: userDefaults)
49 XCTAssert(subject.showOnboarding)
50}
51
52func test_whenCompletedOnboardingTrue_thenShowOnboardingFalse() {
53 userDefaults.set(true, forKey: "completedOnboarding")
54 let subject = ViewModel(userDefaults: userDefaults)
55 XCTAssertFalse(subject.showOnboarding)
56}
57
Some test are asynchronous, which means you need to use XCTExpectation
s to wait for the @Published
value to change:
1func testDefaultValue() {
2 XCTAssertFalse(viewModel.canDoThing)
3}
4func testValueTogglesToTrue() {
5 defaults.canDoThing = true
6 XCTAssertTrue(viewModel.canDoThing)
7extension UserDefaults {
8
9 @objc dynamic public var completedOnboarding: Bool {
10 bool(forKey: "completedOnboarding")
11 }
12}
13class ViewModel: ObservableObject {
14
15 @Published private(set) var showOnboarding: Bool = true
16
17 private let userDefaults: UserDefaults
18
19 public init(userDefaults: UserDefaults) {
20 self.userDefaults = userDefaults
21 self.showOnboarding = !userDefaults.completedOnboarding
22 userDefaults
23 .publisher(for: \.completedOnboarding)
24 .map { !$0 }
25 .receive(on: RunLoop.main)
26 .assign(to: &$showOnboarding)
27 }
28
29 public func completedOnboarding() {
30 userDefaults.set(true, forKey: "completedOnboarding")
31 }
32}
33class MyTestCase: XCTestCase {
34
35 private var userDefaults: UserDefaults!
36 private var cancellables = Set<AnyCancellable>()
37
38 override func setUpWithError() throws {
39 try super.setUpWithError()
40 userDefaults = try XCTUnwrap(UserDefaults(suiteName: #file))
41 userDefaults.removePersistentDomain(forName: #file)
42 }
43
44 // ...
45}
46func test_whenCompletedOnboardingFalse_thenShowOnboardingTrue() {
47 userDefaults.set(false, forKey: "completedOnboarding")
48 let subject = ViewModel(userDefaults: userDefaults)
49 XCTAssert(subject.showOnboarding)
50}
51
52func test_whenCompletedOnboardingTrue_thenShowOnboardingFalse() {
53 userDefaults.set(true, forKey: "completedOnboarding")
54 let subject = ViewModel(userDefaults: userDefaults)
55 XCTAssertFalse(subject.showOnboarding)
56}
57func test_whenCompleteOnboardingCalled_thenShowOnboardingFalse() {
58 let subject = ViewModel(userDefaults: userDefaults)
59 // first define the expectation that showOnboarding will change to false (1)
60 let showOnboardingFalse = expectation(
61 description: "when completedOnboarding called then show onboarding is false")
62
63 // subscribe to showOnboarding publisher to know when the value changes (2)
64 subject
65 .$showOnboarding
66 .filter { !$0 }
67 .sink { _ in
68 // when false received fulfill the expectation (5)
69 showOnboardingFalse.fulfill()
70 }
71 .store(in: &cancellables)
72
73 // trigger the function that changes the value (3)
74 subject.completedOnboarding()
75 // tell the tests to wait for your expectation (4)
76 waitForExpectations(timeout: 0.1)
77}
78
Community Discussions contain sources that include Stack Exchange Network
Tutorials and Learning Resources in Unit Testing
Tutorials and Learning Resources are not available at this moment for Unit Testing