Skip to content

Commit 8876085

Browse files
authored
Switch all tests to nose2 (#127)
- Convert all test files to be nose2-compatible. - Add nose2 configuration file. - Update Gitlab CI file. - Use Python 3.11 instead of Python 3.9. - Use nose2 for testing instead of nose. - Update README.rst. - Add PyPI badge. - Fix conda badge.
1 parent 7ed3a97 commit 8876085

8 files changed

+893
-1018
lines changed

.gitlab-ci.yml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,19 @@ stages:
44
- test
55

66
variables:
7-
PYVERSION: "3.9"
8-
NOSE_WITH_COV: "1"
9-
NOSE_COVER_PACKAGE: "factor_analyzer"
7+
PYVERSION: "3.11"
108
LOGCAPTURE_LEVEL: "DEBUG"
119
CODECOV_TOKEN: "034f0bb1-e590-406f-820c-4e7c41b17712"
1210

1311
# set up the basic job
1412
.runtests:
1513
before_script:
16-
- "conda create --prefix /root/factordev --channel conda-forge --file requirements.txt python=${PYVERSION} curl nose nose-cov --yes --quiet"
14+
- "conda create --prefix /root/factordev --channel conda-forge --file requirements.txt python=${PYVERSION} curl nose2 coverage --yes --quiet"
1715
- /root/factordev/bin/pip install -e .
1816
- /root/factordev/bin/curl -o /root/factordev/bin/codecov https://uploader.codecov.io/latest/linux/codecov
1917
- chmod +x /root/factordev/bin/codecov
2018
script:
21-
- "/root/factordev/bin/nosetests ${TESTFILES}"
19+
- "/root/factordev/bin/nose2 -s tests ${TESTFILES}"
2220
- "/root/factordev/bin/coverage xml"
2321
after_script:
2422
- /root/factordev/bin/codecov
@@ -27,12 +25,12 @@ variables:
2725
testset1:
2826
extends: ".runtests"
2927
variables:
30-
TESTFILES: "tests/test_expected_confirmatory_factor_analyzer.py tests/test_factor_analyzer.py"
28+
TESTFILES: "test_expected_confirmatory_factor_analyzer test_factor_analyzer"
3129
stage: "test"
3230

3331
# second set of test files
3432
testset2:
3533
extends: ".runtests"
3634
variables:
37-
TESTFILES: "tests/test_expected_factor_analyzer.py tests/test_expected_rotator.py tests/test_utils.py"
35+
TESTFILES: "test_expected_factor_analyzer test_expected_rotator test_utils"
3836
stage: "test"

README.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ FactorAnalyzer
99
:target: https://codecov.io/gh/EducationalTestingService/factor_analyzer
1010
:alt: Code coverage
1111

12-
.. image:: https://anaconda.org/ets/factor_analyzer/badges/installer/conda.svg
12+
.. image:: https://anaconda.org/ets/factor_analyzer/badges/version.svg
1313
:target: https://anaconda.org/ets/factor_analyzer/
1414
:alt: Conda version
1515

16+
.. image:: https://img.shields.io/pypi/v/factor_analyzer
17+
:target: https://pypi.org/project/factor-analyzer/
18+
:alt: PyPI version
19+
1620
.. image:: https://img.shields.io/readthedocs/factor_analyzer/latest.svg
1721
:target: https://factor-analyzer.readthedocs.io/
1822
:alt: Docs

tests/test_expected_confirmatory_factor_analyzer.py

Lines changed: 68 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
:organization: Educational Testing Service
77
:date: 2022-09-05
88
"""
9+
import unittest
10+
911
import numpy as np
1012
import pandas as pd
11-
from nose.tools import eq_, raises
1213
from numpy.testing import assert_array_equal
1314

1415
from factor_analyzer.confirmatory_factor_analyzer import (
@@ -21,81 +22,69 @@
2122
THRESHOLD = 1.0
2223

2324

24-
def test_11_cfa(): # noqa: D103
25-
26-
json_name_input = "test11"
27-
data_name_input = "test11"
28-
29-
for check in check_cfa(json_name_input, data_name_input, rel_tol=0.1):
30-
assert check >= THRESHOLD
31-
32-
33-
def test_12_cfa(): # noqa: D103
34-
35-
json_name_input = "test12"
36-
data_name_input = "test12"
37-
38-
for check in check_cfa(json_name_input, data_name_input, abs_tol=0.05):
39-
assert check >= THRESHOLD
40-
25+
class TestConfirmatoryFactorAnalysis(unittest.TestCase):
26+
def test_11_cfa(self): # noqa: D103
27+
json_name_input = "test11"
28+
data_name_input = "test11"
4129

42-
def test_13_cfa(): # noqa: D103
30+
for check in check_cfa(json_name_input, data_name_input, rel_tol=0.1):
31+
self.assertGreaterEqual(check, THRESHOLD)
4332

44-
json_name_input = "test13"
45-
data_name_input = "test13"
33+
def test_12_cfa(self): # noqa: D103
34+
json_name_input = "test12"
35+
data_name_input = "test12"
4636

47-
for check in check_cfa(
48-
json_name_input,
49-
data_name_input,
50-
index_col=0,
51-
is_cov=True,
52-
n_obs=64,
53-
rel_tol=0.1,
54-
):
55-
assert check >= THRESHOLD
37+
for check in check_cfa(json_name_input, data_name_input, abs_tol=0.05):
38+
self.assertGreaterEqual(check, THRESHOLD)
5639

40+
def test_13_cfa(self): # noqa: D103
41+
json_name_input = "test13"
42+
data_name_input = "test13"
5743

58-
def test_14_cfa(): # noqa: D103
44+
for check in check_cfa(
45+
json_name_input,
46+
data_name_input,
47+
index_col=0,
48+
is_cov=True,
49+
n_obs=64,
50+
rel_tol=0.1,
51+
):
52+
self.assertGreaterEqual(check, THRESHOLD)
5953

60-
json_name_input = "test14"
61-
data_name_input = "test14"
54+
def test_14_cfa(self): # noqa: D103
55+
json_name_input = "test14"
56+
data_name_input = "test14"
6257

63-
for check in check_cfa(json_name_input, data_name_input, index_col=0, rel_tol=0.1):
64-
assert check >= THRESHOLD
58+
for check in check_cfa(
59+
json_name_input, data_name_input, index_col=0, rel_tol=0.1
60+
):
61+
self.assertGreaterEqual(check, THRESHOLD)
6562

63+
def test_14_cfa_no_model(self): # noqa: D103
64+
X = np.array([[0, 0, 0, 0], [0, 0, 0, 0]])
6665

67-
@raises(ValueError)
68-
def test_14_cfa_no_model(): # noqa: D103
66+
with self.assertRaises(ValueError):
67+
cfa = ConfirmatoryFactorAnalyzer("string_not_model")
68+
cfa.fit(X)
6969

70-
X = np.array([[0, 0, 0, 0], [0, 0, 0, 0]])
70+
def test_14_cfa_bad_bounds(self): # noqa: D103
71+
X = np.array([[0, 0, 0, 0], [0, 0, 0, 0]])
7172

72-
cfa = ConfirmatoryFactorAnalyzer("string_not_model")
73-
cfa.fit(X)
73+
with self.assertRaises(AssertionError):
74+
cfa = ConfirmatoryFactorAnalyzer(bounds=[(0, 1)])
75+
cfa.fit(X)
7476

77+
def test_14_cfa_cov_with_no_obs(self): # noqa: D103
78+
with self.assertRaises(ValueError):
79+
ConfirmatoryFactorAnalyzer(is_cov_matrix=True)
7580

76-
@raises(AssertionError)
77-
def test_14_cfa_bad_bounds(): # noqa: D103
7881

79-
X = np.array([[0, 0, 0, 0], [0, 0, 0, 0]])
80-
81-
cfa = ConfirmatoryFactorAnalyzer(bounds=[(0, 1)])
82-
cfa.fit(X)
83-
84-
85-
@raises(ValueError)
86-
def test_14_cfa_cov_with_no_obs(): # noqa: D103
87-
88-
ConfirmatoryFactorAnalyzer(is_cov_matrix=True)
89-
90-
91-
class TestModelSpecificationParser: # noqa: D101
82+
class TestModelSpecificationParser(unittest.TestCase):
9283
def test_model_spec_str(self): # noqa: D102
93-
9484
ms = ModelSpecification(np.array([[0, 0, 0]]), 3, 1)
95-
assert str(ms).startswith("<ModelSpecification object at ")
85+
self.assertTrue(str(ms).startswith("<ModelSpecification object at "))
9686

9787
def test_model_spec_as_dict(self): # noqa: D102
98-
9988
loadings = np.array([[0, 0, 0]])
10089
n_factors = 3
10190
n_variables = 1
@@ -112,48 +101,52 @@ def test_model_spec_as_dict(self): # noqa: D102
112101
if isinstance(value, np.ndarray):
113102
assert_array_equal(new_dict[key], value)
114103
else:
115-
eq_(new_dict[key], value)
104+
self.assertEqual(new_dict[key], value)
116105

117106
def test_model_spec_parser_from_dict_none(self): # noqa: D102
118107
X = np.array([[0, 0, 0]])
119108
ms = ModelSpecificationParser.parse_model_specification_from_dict(X, None)
120-
assert isinstance(ms, ModelSpecification)
121-
eq_(ms.n_factors, 3)
122-
eq_(ms.n_variables, 3)
109+
self.assertTrue(isinstance(ms, ModelSpecification))
110+
self.assertEqual(ms.n_factors, 3)
111+
self.assertEqual(ms.n_variables, 3)
123112
assert_array_equal(ms.loadings, np.ones((3, 3), dtype=int))
124113

125-
@raises(ValueError)
126114
def test_model_spec_parser_from_dict_error(self): # noqa: D102
127115
X = np.array([[0, 0, 0]])
128-
ModelSpecificationParser.parse_model_specification_from_dict(X, "not_a_model")
116+
with self.assertRaises(ValueError):
117+
ModelSpecificationParser.parse_model_specification_from_dict(
118+
X, "not_a_model"
119+
)
129120

130121
def test_model_spec_parser_from_array_none(self): # noqa: D102
131122
X = np.array([[0, 0, 0]])
132123
ms = ModelSpecificationParser.parse_model_specification_from_array(X, None)
133-
assert isinstance(ms, ModelSpecification)
134-
eq_(ms.n_factors, 3)
135-
eq_(ms.n_variables, 3)
124+
self.assertTrue(isinstance(ms, ModelSpecification))
125+
self.assertEqual(ms.n_factors, 3)
126+
self.assertEqual(ms.n_variables, 3)
136127
assert_array_equal(ms.loadings, np.ones((3, 3), dtype=int))
137128

138129
def test_model_spec_parser_from_array(self): # noqa: D102
139130
X = np.array([[0, 0, 0]])
140131
spec = np.ones((3, 3), dtype=int)
141132
ms = ModelSpecificationParser.parse_model_specification_from_array(X, spec)
142-
assert isinstance(ms, ModelSpecification)
143-
eq_(ms.n_factors, 3)
144-
eq_(ms.n_variables, 3)
133+
self.assertTrue(isinstance(ms, ModelSpecification))
134+
self.assertEqual(ms.n_factors, 3)
135+
self.assertEqual(ms.n_variables, 3)
145136
assert_array_equal(ms.loadings, np.ones((3, 3), dtype=int))
146137

147138
def test_model_spec_parser_from_frame(self): # noqa: D102
148139
X = np.array([[0, 0, 0]])
149140
spec = pd.DataFrame(np.ones((3, 3), dtype=int))
150141
ms = ModelSpecificationParser.parse_model_specification_from_array(X, spec)
151-
assert isinstance(ms, ModelSpecification)
152-
eq_(ms.n_factors, 3)
153-
eq_(ms.n_variables, 3)
142+
self.assertTrue(isinstance(ms, ModelSpecification))
143+
self.assertEqual(ms.n_factors, 3)
144+
self.assertEqual(ms.n_variables, 3)
154145
assert_array_equal(ms.loadings, np.ones((3, 3), dtype=int))
155146

156-
@raises(ValueError)
157147
def test_model_spec_parser_from_array_error(self): # noqa: D102
158148
X = np.array([[0, 0, 0]])
159-
ModelSpecificationParser.parse_model_specification_from_array(X, "not_a_model")
149+
with self.assertRaises(ValueError):
150+
ModelSpecificationParser.parse_model_specification_from_array(
151+
X, "not_a_model"
152+
)

0 commit comments

Comments
 (0)