tests/testthat/test-parseModel.r

context("csem")

### Linear models ==============================================================
## 1. One exogenous and one endogenous construct -------------------------------

# 1.1 Construct of the measurement model forgotten/misspelled
# 1.1 Everything is correctly specified
model1 <- "
# Structural model
EXPE ~ IMAG

# Composite model
EXPE <~ expe1 + expe2
IMAG <~ imag1 + imag2
"


# 1.2 One measurement equation is redundant
model2 <- "
# Structural model
EXPE ~ IMAG

# Composite model
EXPE <~ expe1 + expe2

# Measurement model
IMAG =~ imag1 + imag4
QUAL =~ qual1 + qual3
"

# 1.3. At least one indicator is connected to several constructs
model3 <- "
# Structural model
EXPE ~ IMAG

# Composite model
EXPE <~ expe1 + expe2

# Measurement model
IMAG =~ imag1 + imag4 + expe1
"
# 1.4 Construct of the measurement model forgotten/misspelled
model4 <- "
# Structural model
EXPE ~ IMAG

# Composite model
EXPE <~ expe1 + expe2
"

# 1.5 Measurement errors across blocks 
model5 <- "
# Structural model
EXPE ~ IMAG

# Composite model
EXPE <~ expe1 + expe2
IMAG =~ imag1 + imag2

imag1 ~~ expe1
"

## Tests
test_that("Linear model: incorrectly specified models provide an error", {
  
  expect_error(parseModel(model2))
  expect_error(parseModel(model3))
  expect_error(parseModel(model4))
  expect_error(parseModel(model5))
})

test_that("Linear model: correctly specified models are correctly returned", {
  expect_s3_class(parseModel(model1), "cSEMModel")
  expect_output(str(parseModel(model1)), "List of 13")
})

## 2. Several endogenous and exogenous constructs ------------------------------
# Including
# - Exogenous construct correlations
# - Measurement error correlations

model <- "
# Structural model
QUAL ~ IMAG + VAL + SAT
VAL  ~ IMAG + EXPE
SAT  ~ EXPE

# Composite Model
EXPE <~ expe1 + expe2
IMAG <~ imag1 + imag2

# Measurement model
SAT  =~ sat1 + sat4
QUAL =~ qual1 + qual2 + qual3
VAL  =~ val3 + val4

# Measurement correlation
qual1 ~~ qual2

# Construct correlation
IMAG ~~ EXPE
"

## Tests
test_that("Linear model: correctly specified models are correctly returned", {
  expect_s3_class(parseModel(model), "cSEMModel")
  expect_output(str(parseModel(model)), "List of 13")
})

## 3. Only measurement model (no structural model) -----------------------------
# 3.1 Only one measurement equation
model1 <- "
# Composite model
IMAG <~ imag1 + imag2 + imag3
"

# 3.2 Only measurement equations without construct correlations given
model2 <- "
# Measurement and composite model
EXPE =~ expe1 + expe2
IMAG <~ imag1 + imag2
"

# 3.3 Two measurement equations and correlation given
model3 <- "
# Construct correlations
EXPE ~~ IMAG

# Measurement and composite model
EXPE =~ expe1 + expe2
IMAG <~ imag1 + imag2
"

test_that("Linear model: incorrectly specified models provide an error", {
  expect_error(parseModel(model1))
  expect_error(parseModel(model2))
})

test_that("Linear model: correctly specified models are correctly returned", {
  expect_s3_class(parseModel(model3), "cSEMModel")
  expect_output(str(parseModel(model3)), "List of 13")
})

### Nonlinear models ===========================================================
## 1. One exogenous and one endogenous construct -------------------------------
## 1.1 Everything correctly specified
model1 <- "
# Structural model
EXPE ~ IMAG + IMAG.IMAG

# Measurement model
EXPE <~ expe1 + expe2
IMAG <~ imag1 + imag2
"

## 1.2 An interaction term does not appears individually in the structural model
model2 <- "
# Structural model
EXPE ~ IMAG + IMAG.IMAG + QUAL.QUAL

# Measurement model
EXPE <~ expe1 + expe2
IMAG =~ imag1 + imag4
QUAL =~ qual1 + qual3
"

## 1.3 Interaction term as a dependent variable in a measurement equation
model3 <- "
# Structural model
QUAL ~ IMAG + IMAG.IMAG + EXPE + EXPE.EXPE + EXPE.IMAG

# Measurement model
EXPE      <~ expe1 + expe2
IMAG      =~ imag1 + imag2
IMAG.IMAG =~ imag3 + imag4
QUAL      =~ qual1 + qual2
"

## 1.4 Interaction term as a dependent variable in a structural equation
model4 <- "
# Structural model
EXPE ~ IMAG + IMAG.IMAG + QUAL.QUAL
QUAL.QUAL ~ IMAG

# Measurement model
EXPE <~ expe1 + expe2
IMAG =~ imag1 + imag4
QUAL =~ qual1 + qual3
"

# 1.5 Construct of the measurement model forgotten/misspelled
model5 <- "
# Structural model
EXPE ~ IMAG + IMAG.IMAG

# Measurement model
EXPE <~ expe1 + expe2
"

# 1.6 Correlation between exogenous construct and interaction term is specified
model6 <- "
# Structural model
EXPE ~ IMAG + IMAG.IMAG

EXPE ~~ IMAG.IMAG

# Measurement model
EXPE <~ expe1 + expe2
IMAG <~ imag1 + imag2
"

## Tests

test_that("Nonlinear model: incorrectly specified models provide an error", {
  
  expect_error(parseModel(model2))
  expect_error(parseModel(model3))
  expect_error(parseModel(model4))
  expect_error(parseModel(model5))
  expect_error(parseModel(model6))
})

## Tests
test_that("Nonlinear model: correctly specified models are correctly returned", {
  expect_s3_class(parseModel(model1), "cSEMModel")
  expect_output(str(parseModel(model1)), "List of 13")
})

## 2. Several endogenous and exogenous constructs ------------------------------
model <- "
# Structural model
QUAL ~ IMAG + VAL + SAT + SAT.SAT
VAL  ~ IMAG + EXPE + EXPE.IMAG
SAT  ~ EXPE

# Measurement model
EXPE <~ expe1 + expe2
IMAG <~ imag1 + imag2
SAT  =~ sat1 + sat4
QUAL =~ qual1 + qual2
VAL  =~ val3 + val4
"

## Tests
test_that("Nonlinear model: correctly specified models are correctly returned", {
  expect_s3_class(parseModel(model), "cSEMModel")
  expect_output(str(parseModel(model)), "List of 13")
})

### Second-order model =========================================================
## 1. One second order construct -----------------------------------------------
# 1.1 One measurement equation for a construct used to define the second order 
#     construct got forgotten but appears in the structural model (IMAG)

model1 <- "
# Structural model
SAT ~ QUAL
VAL ~ SAT + SAT.SAT + IMAG

# Measurement model
EXPE <~ expe1 + expe2
SAT =~ sat1 + sat2
VAL =~ val1 + val2
QUAL =~ IMAG + EXPE
"

# 1.2 One measurement equation for a construct used to define the second order 
#     construct got forgotten and does not (!) appear in the structural model

model2 <- "
# Structural model
SAT ~ QUAL
VAL ~ SAT + SAT.SAT

# Measurement model
EXPE <~ expe1 + expe2
SAT =~ sat1 + sat2
VAL =~ val1 + val2
QUAL =~ IMAG + EXPE
" # not detected by parseModel() but csem() throws an error

# 1.3. One second-order construct has also indicators attached
model3 <- "
# Structural model
SAT ~ QUAL
VAL ~ SAT + SAT.SAT

# Measurement model
EXPE <~ expe1 + expe2
SAT =~ sat1 + sat2
VAL =~ val1 + val2
QUAL =~ IMAG + EXPE + imag3
IMAG <~ imag1 + imag2
" # not detected by parseModel() but csem() throws an error

# 1.4 Second order construct appears as an interaction term with itself 
#     in the structural model

model4 <- "
# Structural model
SAT ~ QUAL + QUAL.QUAL
VAL ~ SAT + SAT.SAT

# Measurement model
EXPE <~ expe1 + expe2
SAT =~ sat1 + sat2
VAL =~ val1 + val2
QUAL =~ IMAG + EXPE
IMAG <~ imag1 + imag2
"

# 1.5 Second order construct appears as an interaction term with another 
#     first order constructs in the structural model

model5 <- "
# Structural model
SAT ~ QUAL
VAL ~ SAT + SAT.SAT
LOY ~ QUAL + VAL + QUAL.VAL

# Measurement model
EXPE <~ expe1 + expe2
SAT =~ sat1 + sat2
VAL =~ val1 + val2
LOY =~ loy1 + loy2
QUAL =~ IMAG + EXPE
IMAG <~ imag1 + imag2
"

# 1.6 "Normal" second order model including a nonlinear term

model6 <- "
# Structural model
SAT ~ QUAL
VAL ~ SAT + SAT.SAT

# Measurement model
EXPE <~ expe1 + expe2

SAT =~ sat1 + sat2
VAL =~ val1 + val2
QUAL =~ IMAG + EXPE
IMAG <~ imag1 + imag2
"

## Tests

test_that("Second-order model: incorrectly specified models provide an error", {
  
  expect_error(parseModel(model1))
  expect_error(csem(satisfaction, model2, .normality = FALSE))
  expect_error(csem(satisfaction, model3, .normality = FALSE))
})

## Tests
test_that("Second-order model: correctly specified models are correctly returned", {
  expect_s3_class(parseModel(model4), "cSEMModel")
  expect_output(str(parseModel(model4)), "List of 13")

  expect_s3_class(parseModel(model5), "cSEMModel")
  expect_output(str(parseModel(model5)), "List of 13")

  expect_s3_class(parseModel(model6), "cSEMModel")
  expect_output(str(parseModel(model6)), "List of 13")
})


## 2. Two second-order construct-----------------------------------------------
## 2.1 Everything correctly specified
model1 <- "
# Structural model
SAT ~ QUAL
VAL ~ SAT + SAT.SAT + LOY

# Measurement model
EXPE <~ expe1 + expe2
SAT =~ sat1 + sat2
VAL =~ val1 + val2
HELP <~ val3 + val4
QUAL =~ IMAG + EXPE
LOY =~ HELP
IMAG <~ imag1 + imag2
"

## 2.2 Two second-order construct with at least one 2nd order construct attatched
##     to a nonlinear term
model2 <- "
# Structural model
SAT ~ QUAL
VAL ~ SAT + SAT.SAT + LOY + LOY.SAT

# Measurement model
EXPE <~ expe1 + expe2
SAT =~ sat1 + sat2
VAL =~ val1 + val2
HELP <~ val3 + val4
QUAL =~ IMAG + EXPE + IMAG.IMAG + EXPE.IMAG
LOY =~ HELP
IMAG <~ imag1 + imag2
"

# 2.3 Construct order > 2 

model3 <- "
# Structural model
SAT ~ QUAL
VAL ~ SAT + SAT.SAT + LOY

# Measurement model
EXPE <~ expe1 + expe2
SAT =~ sat1 + sat2
VAL =~ val1 + val2
HELP =~ val3 + val4
QUAL =~ IMAG + EXPE + LOY
LOY =~ HELP
IMAG <~ imag1 + imag2
"

## Tests

test_that("Second-order model: incorrectly specified models provide an error", {
  expect_error(parseModel(model2))
  expect_error(parseModel(model3))
})

## Tests
test_that("Second-order model: correctly specified models are correctly returned", {
  expect_s3_class(parseModel(model1), "cSEMModel")
  expect_output(str(parseModel(model1)), "List of 13")
})

## .check errors argument ======================================================
models <- list(
  'C ~ A', # single path
  "B =~ x21", # single loading
  "D <~ x41", # single weight
  "A ~ C", # wrong single path, both constructs exist
  "D ~ E", # wrong path as construct E does not exist
  "B =~ x31",# wrongly assigned indicator
  "D <~ x51" # Indicator that does not exist
  )

for(i in seq_along(models)) {
  test_that(paste0("Model: ", models[[i]], " does not throw an error when .check_errors = FALSE."), {
    expect_s3_class(parseModel(models[[i]], .check_errors = FALSE), "cSEMModel")
    expect_output(str(parseModel(models[[i]], .check_errors = FALSE)), "List of 13")
  })
}

Try the cSEM package in your browser

Any scripts or data that you put into this service are public.

cSEM documentation built on Nov. 25, 2022, 1:05 a.m.