# Unit tests of matrix arithmetic

Is this good way of creating tests? I have no experience how they do it in companies. Ignore uppercase names of variables.. I may change it eventually.

reduction_strategy.py defines my ListArray class, which I've written unit tests for.

ListArray defines object that takes care of matrices containing symbolic elements. Basic matrix operations adding and multiplying, syntax for using this object slicing, Changing rows and columns. This class let me keep 2d object, and make slice's easier, Matrix object has indexing from 0 to n as 1d always.

Examples:

$A = \begin{bmatrix} 'a' & 'b'\\ 'c' & 'd' \end{bmatrix} , B = \begin{bmatrix} 'e' & 'f'\\ 'g' & 'h' \end{bmatrix} , A + B = \begin{bmatrix} 'a + e' & 'b + f'\\ 'c + g' & 'd + h' \end{bmatrix}$ $A = \begin{bmatrix} 'a' & 'b'\\ 'c' & 'd' \end{bmatrix} , B = \begin{bmatrix} 'e' & 'f'\\ 'g' & 'h' \end{bmatrix} , A * B = \begin{bmatrix} 'a*e + b*g' & 'a*f + b*h'\\ 'c*e + d*g' & 'c*f + d*h' \end{bmatrix}$

# reduction_strategy.py
import numpy as np
from copy import copy, deepcopy
from sympy import Matrix, zeros, ones, eye, diag
from itertools import permutations

class ListArray:
def __init__(self, array):
# self.__class__ = 'ListArray'
if type(array) == int:
array = [array]

if type(array[0]) != list:
self.single = True
self.size = (1, len(array))
array = [array]  # Matrix always uses 2d !
else:
self.single = False
self.size = (len(array), len(array[0]))

if len(array) == 1:
self.single = True

for row in array:
if type(row[0]) == list:
print('To deep Array:', array)
break

self.array = Matrix(array)
self.history = [{'created': array}]

return ListArray([[
self.array[r * self.size[1] + c] +
other.array[r * self.size[1] + c]
for c in range(self.size[1])
]
for r in range(self.size[0])
])

def __eq__(self, other):
return self.array == other.array

def __getitem__(self, i):
# Condition to extract row!
if type(i) is slice:
rstart = i.start if i.start else 0
rstop = i.stop if i.stop else self.size[0]
rstep = i.step if i.step else 1

cstart = 0
cstop = self.size[1]
cstep = 1

# Condition to Slice with number -> one Row
elif type(i) is int:
rstart = i
rstop = i + 1
rstep = 1

cstart = 0
cstop = self.size[1]
cstep = 1

# Condition Double slice
elif type(i[0]) is slice and type(i[1]) is slice:
rslice = i[0]
cslice = i[1]

rstart = rslice.start if rslice.start else 0
rstop = rslice.stop if rslice.stop else self.size[0]
rstep = rslice.step if rslice.step else 1
cstart = cslice.start if cslice.start else 0
cstop = cslice.stop if cslice.stop else self.size[1]
cstep = cslice.step if cslice.step else 1
else:
raise ValueError(f"Incorrect Slice params: {i}")

array = self.array
if self.single:
# array = array]
out = [
array[r * self.size[1] + c]
for c in range(cstart, cstop, cstep)
for r in range(rstart, rstop, rstep)
]
else:
out = [[
array[r * self.size[1] + c]
for c in range(cstart, cstop, cstep)
]
for r in range(rstart, rstop, rstep)
]

return ListArray(out)

def __repr__(self):
txt = '['
for row in range(self.size[0]):
if row == 0:
txt += f"{'[':>1}"
else:
txt += f'\n{"[":>3}'

for col in range(self.size[1]):
txt += f' {self.array[self.size[1]*row +col]},'

if row >= self.size[0] - 1:
txt += ']'
else:
txt += f'],'
txt += f']'
return txt

def __sub__(self, other):
return ListArray([[
self.array[r * self.size[1] + c] -
other.array[r * self.size[1] + c]
for c in range(self.size[1])
]
for r in range(self.size[0])
])

# def __class__(self):
#     pass

def __len__(self):
# print(len(self.array))
if self.single:
return self.size[1]
return self.size[0]

def __mul__(self, other):
result = self.array * other.array
result = [result[other.size[1]*row: other.size[1]*row + other.size[1]]
for row in range(self.size[0])
]
return ListArray(result)

def switch_Rows(self):
print("Finish this")

def switch_cols(self):
print("Finish this")

def transp(self):
c = self.size[0]
r = self.size[1]
array_t = self.array.T
return ListArray([array_t[row*c:row*c + c] for row in range(r)])

def match_patern_any(self):
# A | B
# --|--
# C | D
if self.size[0] % 2 == 1 or self.size[1] % 2 == 1:
raise ValueError("Matrix has to be even size!")

half_rows = int(self.size[0]/2)
half_cols = int(self.size[1]/2)

square_a = self[0:half_rows, 0:half_cols]
square_b = self[0:half_rows, half_cols:self.size[1]]
square_c = self[half_rows:self.size[0], 0:half_cols]
square_d = self[half_rows:self.size[0], half_cols:self.size[1]]

return any([
square_a == square_b,
square_a == square_c,
square_a == square_d,
square_b == square_c,
square_b == square_d,
square_c == square_d
])

class Reduktor(ListArray):
def __init__(self, matrix_input):
ListArray.__init__(self, array=matrix_input)

assert (self.size[0] == self.size[0] and self.size[0] % 2 == 0)

print("R:", len(matrix_input))
print("C:", len(matrix_input[0]))
self.matrix = matrix_input

def col_order(self):
pass

if __name__ == "__main__":
a = [['a', 'b', 'c', 'g'],
['d', 'e', 'f', 'x'],
['q', 't', 'i', 'y'],
['i', 'o', 'p', 's']]

b = [['d', 'e', 'f'], ['g', 'h', 'i']]
c = ['x', 'y', 'z']
d = [['i', 'b', 'c', 'd']]

A = ListArray(a)
print(type(A))
red = Reduktor(a)


testing_ListArray.py contains the unit tests:

# testing_ListArray.py
from reduction_strategy import ListArray
import unittest

class TestinglistArray(unittest.TestCase):
def setUp(self):
self.a = \
[['a', 'b', 'c', 'g'],
['d', 'e', 'f', 'x'],
['q', 't', 'i', 'y'],
['i', 'o', 'p', 's']]

self.b = \
[['a', 'c', 'j', 'v'],
['d', 'e', 'f', 'x'],
['q', 't', 'i', 'y'],
['i', 'o', 'p', 's']]

def test_equal(self):

A = ListArray(self.a)
B = ListArray(self.b)
Aprim = ListArray(self.a)
cut_A = A[2:, 0:2]
cut_B = B[2:, 0:2]

assert A == Aprim
assert not A == B
assert cut_A == cut_B

assert A[2] == ListArray([['q', 't', 'i', 'y']])
assert A[2] == ListArray(['q', 't', 'i', 'y'])

def test_fit(self):
a = \
[['a', 'b', 'a', 'b'],
['a', 'b', 'a', 'b']]

b = [['a', 'b'],
['c', 'd']]
c = ['a', 'b', 'c']

A = ListArray(a)
B = ListArray(b)
C = ListArray(c)

assert A.match_patern_any()
assert not B.match_patern_any()
self.assertRaises(ValueError, C.match_patern_any)

def test_traspose(self):
a = [['a', 'g'],
['b', 'x']]
b = [['a', 'b'],
['g', 'x']]

a2 = [['a', 'g', 'j'],
['b', 'x', 'k']]

b2 = [['a', 'b'],
['g', 'x'],
['j', 'k']]

A = ListArray(a)
B = ListArray(b)

A2 = ListArray(a2)
B2 = ListArray(b2)
assert A.transp() == B  # Transposed A = A'
assert A2.transp() == B2 # Transposed A2 = A2'

def test_mult(self):
a = [['a', 'g'],
['b', 'x']]

b = [['b', 'b'],
['d', 'b']]
res1 = [['a*a + b*g', 'a*g + x*g'],
['b*a + x*b', 'g*b + x*x']]
res2 = [['a*b + d*g', 'a*b + b*g'],
['b*b + d*x', 'b*b + x*b']]

A = ListArray(a)
B = ListArray(b)
Res1 = ListArray(res1)
Res2 = ListArray(res2)

assert(A*A == Res1)
assert(A*B == Res2)

def test_arra_length(self):
A = ListArray(self.a)
assert A.size[0] == len(A)
assert A.size[0] == 4
assert A.size[1] == len(A[0])
assert A.size[1] == 4

if __name__ == '__main__':
unittest.main()


• Welcome to CR! You'll want to edit your post to tell reviewers more about what ListArray does, make the title of your post describe the purpose of that code, and then clean up the variable names, because reviewers are expecting to see your finished, working, production-ready code. As it stands, there's very little context, and it's debatable whether there's enough for reviewers to go on: FYI a reviewer has voted to close for lack of concrete context. Please take a few moments to fix the typos and add the missing information; reviewers will be spending much more time than that reviewing. Commented Dec 2, 2019 at 19:11
• But it it is just about reviewing second script, I could not attach main script... Commented Dec 2, 2019 at 20:54

If you are using the unittest module, you should use it to its full potential. You are already using assertRaises, but there is also assertEqual, assertTrue and many more. The difference is in the amount of information you get when the test case fails:

import unittest

class Test(unittest.TestCase):
def setUp(self):
self.a, self.b = [1, 2, 3], [2, 3, 4]

def test_a(self):
assert self.a == self.b

def test_b(self):
self.assertEqual(self.a, self.b)

if __name__ == "__main__":
unittest.main()


produces this output:

\$ python3 /tmp/test.py
FF
======================================================================
FAIL: test_a (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 7, in test_a
assert self.a == self.b
AssertionError

======================================================================
FAIL: test_b (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 10, in test_b
self.assertEqual(self.a, self.b)
AssertionError: Lists differ: [1, 2, 3] != [2, 3, 4]

First differing element 0:
1
2

- [1, 2, 3]
+ [2, 3, 4]

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=2)


Note how test_a, which uses assert like your code, gives you almost no information on what went wrong, whereas test_b tells you very explicitly what went wrong.