2
\$\begingroup\$

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:

Matrix equations Multiply

# 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}]

    def __add__(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 __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()

New contributor
Grzegorz Krug is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
\$\endgroup\$
  • 3
    \$\begingroup\$ 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. \$\endgroup\$ – Mathieu Guindon Dec 2 at 19:11
  • \$\begingroup\$ But it it is just about reviewing second script, I could not attach main script... \$\endgroup\$ – Grzegorz Krug Dec 2 at 20:54
4
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Yes, thanks, I have forget about it :) \$\endgroup\$ – Grzegorz Krug 2 days ago

Your Answer

Grzegorz Krug is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.