I'm confused about the order in which lifecycle hook in Kotest are executed, especially when it comes to nested tests. Consider the following example:
import io.kotest.core.spec.style.DescribeSpec
class KotestTest : DescribeSpec({
beforeSpec { println("beforeSpec") }
afterSpec { println("afterSpec") }
beforeAny { println("beforeAny") }
afterAny { println("afterAny") }
beforeContainer { println("beforeContainer") }
afterContainer { println("afterContainer") }
beforeEach { println("beforeEach") }
afterEach { println("afterEach") }
beforeTest { println("beforeTest") }
afterTest { println("afterTest") }
describe("1") {
println("1")
beforeAny { println("beforeAny 1") }
afterAny { println("afterAny 1") }
beforeContainer { println("beforeContainer 1") }
afterContainer { println("afterContainer 1") }
beforeEach { println("beforeEach 1") }
afterEach { println("afterEach 1") }
beforeTest { println("beforeTest 1") }
afterTest { println("afterTest 1") }
describe("2") {
println("2")
beforeAny { println("beforeAny 2") }
afterAny { println("afterAny 2") }
beforeContainer { println("beforeContainer 2") }
afterContainer { println("afterContainer 2") }
beforeEach { println("beforeEach 2") }
afterEach { println("afterEach 2") }
beforeTest { println("beforeTest 2") }
afterTest { println("afterTest 2") }
it("3") { println("3") }
}
}
})
When I run this test with Kotest 5.4.1, I get the following result:
beforeSpec
beforeContainer
beforeAny
beforeTest
1
beforeContainer
beforeContainer 1
beforeAny
beforeAny 1
beforeTest
beforeTest 1
2
beforeEach
beforeEach 1
beforeEach 2
beforeAny
beforeAny 1
beforeAny 2
beforeTest
beforeTest 1
beforeTest 2
3
afterTest
afterTest 1
afterTest 2
afterAny
afterAny 1
afterAny 2
afterEach
afterEach 1
afterEach 2
afterTest
afterTest 1
afterAny
afterAny 1
afterContainer
afterContainer 1
afterTest
afterAny
afterContainer
afterSpec
Now for me the following questions arise:
Is there some documentation or guarantee about the order of beforeContainer, beforeAny and beforeTest resp. beforeEach, beforeAny and beforeTest?
I understand that the before hooks are executed from outside to inside, but would expect the after hooks to then be executed in the reverse order, i.e., from inside to outside. Is this a bug?
Motivation: We wanted to verify mock invocations like
val mock = mockk<MyClass>()
afterEach { confirmVerified(mock); clearMocks(mock); }
describe("fun1") {
beforeEach { every { mock.fun1()} returns 1 }
afterEach { verify { mock.fun1()} }
it("...") {...}
}
but due to the execution order the outer afterEach gets executed first.
The order of lifecycle hooks in Kotest is confusing because one would expect that they register the hooks somewhere inside a scoped context, but instead it is registered in the spec.
You can check this by calling
println(registeredExtensions())
in any test case. The function registeredExtensions() gives you the list of all extensions that are registered in your spec, and that list will contain BeforeX and AfterX extensions in the same order, you called functions beforeX and afterX, respectively.
The problem here is that all extensions are always executed in the order they are registered. This is okay for all before-functions, but is not okay for the after-functions.
The simplest way I found to solve this, is to write a TestCaseExtension that allows to wrap something around the execution of each test:
class NestedAfter(
private val descriptor: Descriptor,
private val afterTest: AfterTest,
private val type: TestType? = null,
) : TestCaseExtension {
override suspend fun intercept(testCase: TestCase, execute: suspend (TestCase) -> TestResult): TestResult {
return execute(testCase).also { testResult ->
if ((type == null || type == testCase.type) && descriptor.isAncestorOf(testCase.descriptor))
afterTest(Tuple2(testCase, testResult))
}
}
}
The extension executes each test and after that it conditionally calls the afterTest function. Since all extensions are called in order of their registration, each subsequent NestedAfter is wrapped inside preceding NestedAfter calls - leading to a proper nesting and the execution order we are expecting.
The condition descriptor.isAncestorOf(testCase.descriptor) makes sure that we call the afterTest function only for test cases that are (possibly nested) inside the test where we defined the extension.
This would still be quite cumbersome to use, so we also define the following functions:
fun TestScope.nestedAfterAny(afterTest: AfterTest) {
testCase.spec.extension(NestedAfter(testCase.descriptor, afterTest))
}
fun TestScope.nestedAfterEach(afterTest: AfterTest) {
testCase.spec.extension(NestedAfter(testCase.descriptor, afterTest, TestType.Test))
}
fun TestScope.nestedAfterContainer(afterTest: AfterTest) {
testCase.spec.extension(NestedAfter(testCase.descriptor, afterTest, TestType.Container))
}
These function makes sure to register an instance of our extension accompanied by the descriptor of the TestCase that is currently running.
Now, if we want to have the expected execution order, then we can use nestedAfterAny instead of afterAny, nestedAfterContainer instead of afterContainer and nestedAfterEach instead of afterEach.
You can then write the following inside your test class, and the functions will respect the nesting of your test cases:
val mock = mockk<MyClass>()
describe("root") {
nestedAfterEach { confirmVerified(mock); clearMocks(mock); }
describe("fun1") {
beforeEach { every { mock.fun1() } returns 1 }
nestedAfterEach { verify { mock.fun1() } }
it("...") {
mock.fun1()
}
}
}
Note that we can call the nestedAfterX functions only inside TestContext which is why I introduced the describe("root") container in the example above.
If we want to get rid of that limitation, we can alter the NestedAfter class to have a nullable descriptor and consequently change the condition to descriptor == null || descriptor.isAncestorOf(testCase.descriptor).
Then we can define functions
fun DslDrivenSpec.nestedAfterAny(afterTest: AfterTest) {
extension(NestedAfter(null, afterTest))
}
fun DslDrivenSpec.nestedAfterEach(afterTest: AfterTest) {
extension(NestedAfter(null, afterTest, TestType.Test))
}
fun DslDrivenSpec.nestedAfterContainer(afterTest: AfterTest) {
extension(NestedAfter(null, afterTest, TestType.Container))
}
which allow to define nestedAfterX functions on the root level.
Then we can alter your KotestTest from above to look like this:
class KotestTest : DescribeSpec({
beforeSpec { println("beforeSpec") }
afterSpec { println("afterSpec") }
beforeAny { println("beforeAny") }
nestedAfterAny { println("afterAny") }
beforeContainer { println("beforeContainer") }
nestedAfterContainer { println("afterContainer") }
beforeEach { println("beforeEach") }
nestedAfterEach { println("afterEach") }
beforeTest { println("beforeTest") }
nestedAfterEach { println("afterTest") }
describe("1") {
println("1")
beforeAny { println("beforeAny 1") }
nestedAfterAny { println("afterAny 1") }
beforeContainer { println("beforeContainer 1") }
nestedAfterContainer { println("afterContainer 1") }
beforeEach { println("beforeEach 1") }
nestedAfterEach { println("afterEach 1") }
beforeTest { println("beforeTest 1") }
nestedAfterEach { println("afterTest 1") }
describe("2") {
println("2")
beforeAny { println("beforeAny 2") }
nestedAfterAny { println("afterAny 2") }
beforeContainer { println("beforeContainer 2") }
nestedAfterContainer { println("afterContainer 2") }
beforeEach { println("beforeEach 2") }
nestedAfterEach { println("afterEach 2") }
beforeTest { println("beforeTest 2") }
nestedAfterEach { println("afterTest 2") }
it("3") { println("3") }
}
}
})
The output of this test where we replaced all after functions with nestedAfter functions (except for afterSpec where it is not necessary) looks like this:
beforeSpec
beforeContainer
beforeAny
beforeTest
1
beforeContainer
beforeContainer 1
beforeAny
beforeAny 1
beforeTest
beforeTest 1
2
beforeEach
beforeEach 1
beforeEach 2
beforeAny
beforeAny 1
beforeAny 2
beforeTest
beforeTest 1
beforeTest 2
3
afterTest 2
afterEach 2
afterAny 2
afterTest 1
afterEach 1
afterAny 1
afterTest
afterEach
afterAny
afterContainer 1
afterAny 1
afterContainer
afterAny
afterContainer
afterAny
afterSpec
Also please note that afterTest is only a soft-deprecated synonym of afterEach which is why I did not provide a nested counterpart for it.
Related
Hope anybody could guide me here. I spend some hours on it and can't understand what's going on.
Mission: Replace a json element by a jsonpath search tag. (sort of $ref feature)
In my code example below i want to replace the value of DataReaderUser by a value found by the json path search $.UsersAndGroups.Users[?(#.Name == 'OMDASAccountUser')].Username . In this case it should result in the value "contoso\SVCSCOM-DO-OMDAS"
The code below works as expected.. the issue is below this code ..
https://dotnetfiddle.net/gEjggK
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
string json = #"{
""SQLServer"": {
""SQLReportingServices"": {
""AccountSettings"": {
""DataReaderUser"": {""$JsonPath"": ""$.UsersAndGroups.Users[?(#.Name == 'OMDASAccountUser')].Username""},
}
}
},
""UsersAndGroups"": {
""Users"": [
{
""Name"": ""OMActionAccountUser"",
""Username"": ""contoso\\SVCSCOM-DO-OMDAS"",
},
{
""Name"": ""OMDASAccountUser"",
""Username"": ""contoso\\SVCSCOM-DO-OMDAS"",
}
]
}
}";
JObject jo = JObject.Parse(json);
var JsonPath = jo.SelectToken("..$JsonPath");
JsonPath.Parent.Parent.Replace(jo.SelectToken(JsonPath.ToString()));
Console.WriteLine(jo.ToString());
}
}
The output will be :
{
"SQLServer": {
"SQLReportingServices": {
"AccountSettings": {
"DataReaderUser": "contoso\\SVCSCOM-DO-OMDAS"
}
}
},
"UsersAndGroups": {
"Users": [
{
"Name": "OMActionAccountUser",
"Username": "contoso\\SVCSCOM-DO-OMDAS"
},
{
"Name": "OMDASAccountUser",
"Username": "contoso\\SVCSCOM-DO-OMDAS"
}
]
}
}
Now the issue:
I want to do the same for all possible jsonpaths refers. So i use the SelectTokens and an foreach . But it looks like the behavior is different , the parents are null.
https://dotnetfiddle.net/lZW3XP
using System;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
string json = #"{
""SQLServer"": {
""SQLReportingServices"": {
""AccountSettings"": {
""DataReaderUser"": {""$JsonPath"": ""$.UsersAndGroups.Users[?(#.Name == 'OMDASAccountUser')].Username""},
}
}
},
""UsersAndGroups"": {
""Users"": [
{
""Name"": ""OMActionAccountUser"",
""Username"": ""contoso\\SVCSCOM-DO-OMDAS"",
},
{
""Name"": ""OMDASAccountUser"",
""Username"": ""contoso\\SVCSCOM-DO-OMDAS"",
}
]
}
}";
JObject jo = JObject.Parse(json);
var JsonPaths = jo.SelectTokens("..$JsonPath");
foreach (var JsonPath in JsonPaths )
{
JsonPath.Parent.Parent.Replace(jo.SelectToken(JsonPath.ToString()));
}
Console.WriteLine(jo.ToString());
}
}
And the output:
Run-time exception (line 34): Object reference not set to an instance of an object.
Stack Trace:
[System.NullReferenceException: Object reference not set to an instance of an object.]
at Newtonsoft.Json.Linq.JsonPath.PathFilter.GetNextScanValue(JToken originalParent, JToken container, JToken value)
at Newtonsoft.Json.Linq.JsonPath.ScanFilter.<ExecuteFilter>d__4.MoveNext()
at Program.Main() :line 34
would be great to get some directions since i am spinning my head here.
michel
SelectTokens uses lazy evaluation and if you modify the token while enumerating all matches it can break in unexpected ways. A simple fix is to add ToArray() to force eager evaluation:
var JsonPaths = jo.SelectTokens("..$JsonPath").ToArray();
There is a way to map json objects in react-native? For example, let's say I receive the following object:
{
"dados": {
"ultimaAtualizacao": {
"nome": "Lala"
}
"nascimento": "01/01/2001"
}
}
I want to map it like this:
{
"data": {
"lastUpdate": {
"name": "Lala"
}
"dob": "01/01/2001"
}
}
Considering that fetch returns the first object, this would be possible doing the following code:
myService.get(url).then(response => this.mapObject(response, []));
//Considering state is initialized and I have the mapedEntity to return correct properties
mapObject(jsonObject, parentProperty) {
for (let property in jsonObject) {
if (Array.isArray(jsonObject[property])) {
this.mapArray(jsonObject[property], [...parentProperty,property]); //Would be something like mapObject function
} else if (typeof jsonObject[property] === 'object') {
this.mapObject(jsonObject[property],[...parentProperty,property]);
} else {
let prop = this.state;
for(let k of parentProperty) {
prop = prop[mapedEntity[k]];
}
prop[entidadeMapeada[property]] = jsonObject[property];
}
}
}
There is someway simplier to achieve this?
I have a mongo collection where documents have aprox the following structure:
item{
data{"emailBody":
"{\"uniqueKey\":\" this is a stringified json\"}"
}
}
What I want to do is to use 'uniqueKey' as an indexed field, to make an "inner join" equivalant with items in a different collection.
I was thinking about running a loop on all the documents -> parsing the json -> Saving them as new property called "parsedEmailBody".
Is there a better way to handle stringified json in mongo?
The only way is to loop through the collection, parse the field to JSON and update the document in the loop:
db.collection.find({ "item.data.emailBody": { "$type": 2 } })
.snapshot().forEach(function(doc){
parsedEmailBody = JSON.parse(doc.item.data.emailBody);
printjson(parsedEmailBody);
db.collection.updateOne(
{ "_id": doc._id },
{ "$set": { "item.data.parsedEmailBody": parsedEmailBody } }
);
});
For large collections, leverage the updates using the Bulk API:
var cursor = db.collection.find({ "item.data.emailBody": { "$type": 2 } }).snapshot(),
ops = [];
cursor.forEach(function(doc){
var parsedEmailBody = JSON.parse(doc.item.data.emailBody);
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "item.data.parsedEmailBody": parsedEmailBody } }
}
});
if (ops.length === 500) {
db.collection.bulkWrite(ops);
ops = [];
}
});
if (ops.length > 0) { db.collection.bulkWrite(ops); }
I am trying to do a simple POST to save a new object
class diagram
I want to POST a Question with the following attributes:
String name
static hasOne = [questionType: QuestionType]
Each Question has one abstract QuestionType extended by one of this objects:
Mcq (Multiple Choice Question)
SourceCode
Matching
...
For my Mcq, I have the following attributes:
static belongsTo = [questionType: QuestionType]
static hasMany = [propositions: Proposition]
static constraints = {
}
static mapping = {
propositions cascade: 'all-delete-orphan'
}
So my Mcq owns 0 to many Proposition
A Proposition is made of:
String body
Boolean isCorrect
String reason
I am using Postman to check my API. I don't have any UI for now.
My Question, Qcm and Proposition objects have a controller and a service to manage them.
My QuestionService:
class QuestionService {
def save(Question question) {
if (question == null) {
transactionStatus.setRollbackOnly()
render status: NOT_FOUND
return
}
if (question.hasErrors()) {
transactionStatus.setRollbackOnly()
respond question.errors, view:'create'
return
}
question.save flush:true
}
def update(Question question) {
if (question == null) {
transactionStatus.setRollbackOnly()
render status: NOT_FOUND
return
}
if (question.hasErrors()) {
transactionStatus.setRollbackOnly()
respond question.errors, view:'edit'
return
}
question.save flush:true
}
def delete(Question question) {
if (question == null) {
transactionStatus.setRollbackOnly()
render status: NOT_FOUND
return
}
question.delete flush: true
}
}
My QuestionController:
#Secured('ROLE_ADMIN')
#Transactional(readOnly = true)
class QuestionController {
//spring bean injected for the Controller to access the Service
def questionService
static responseFormats = ['json', 'xml']
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
//views
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Question.list(params), model:[questionCount: Question.count()]
}
def show(Question question) {
respond question
}
//actions
def save(Question question){
try { questionService.save question }
catch (e){ respond status: PRECONDITION_FAILED }
respond question, [status: CREATED, view:"show"]
}
def update(Question question) {
try { questionService.update question }
catch (e) { respond status: PRECONDITION_FAILED }
respond question, [status: OK, view:"show"]
}
def delete(Question question) {
try { questionService.delete question }
catch (e){ respond status: PRECONDITION_FAILED }
respond status: OK, view:"index"
}
}
I can get each Proposition, Mcq and Question with my index and show actions.
I don't allow my users to POST a new Proposition / Mcq. If they want to do it, they have to UPDATE the full Question itself. So only my Question can be modified (and modify others), others or in read-only.
My problem is that I can NOT POST this:
{
"name": "name1",
"questionType": {
"propositions": [
{
"body": "body proposition 1",
"isCorrect": true,
"reason": "reason proposition 1"
},
{
"body": "body proposition 2",
"isCorrect": false,
"reason": "reason proposition 2"
}
]
}
}
It always returns me a
[
null
]
But this works:
{
"name": "name1",
"questionType": {
"id": 1
}
}
So, in here, I can refer to a QuestionType already in my database (from my bootstrap) but I don't see how to create in cascade all of my Question, QuestionType, Qcm and Proposition.
my json structure is:
{
"bob": {"blue": 2 }
}
next i did this for checking does not exists properties:
if(myArray['alice']['red] === false) {
console.log('undefined');
}
then it returns "Cannot read property 'red' of undefined"
try this code:
var arr = {
"bob": {"blue": 2 }
}
if(!arr['alice']) {
console.log('undefined');
}
else if (!arr['alice']['red']) {
console.log('undefined');
}