Sunday, June 17, 2018

Pragmatic unit testing - When to test a private method

Let's say you have (pseudo) code like this:
public List<Items> GetData(Query Parameters)
{
     1     open SQLConnection
     2     execute SQLQuery using Query Parameters
     3     ProcessDataBeforeReturning(data)
     4     return the data
}
private List<Items> ProcessDataBeforeReturning(data)
{
//process the data
return processed data
}

Which part of this should be tested?
Line 3 - processing the data after it returns. 

Approach 1 - Brute force - Inject dependencies
1. Inject the dependencies via constructor, parameter, or method injection
2. In the test, fake the dependencies so that they'll return some fake data
3. Call GetData and verify that the end results are what were expected

This is a pretty good idea if doing so isn't going to require a huge refactoring. 

Approach 2 - Test the private method
1. Make ProcessDataBeforeReturning a public method
2. Directly test it

This is less work than the first approach, and localizes the changes. The first approach probably would result in a pretty big refactoring in a real system.

Approach 3 - Extract a new class 
1. Create a new class and put ProcessDataBeforeReturning() in it
2. Directly test the method

This is better than approach 2. The first class has one job: get data. The second class has one job: process data. Now the caller of GetData() can call ProcessDataBeforeReturning() on the data returned from GetData(). 

Conclusion
I would almost always do Approach 1. I would choose Approach 3 if Approach 1 would require a large refactoring. 

Saturday, June 16, 2018

Python generator functions

Consider the simple problem of summing between A and B (ignore the fact that there's a formula for this that would be much more efficient than looping)

Using range
def get_sum(a,b):
       return sum(range(a, b+1))

The problem with this is if the difference between a and b is huge, you are wasting a ton of memory. range() returns a list obj. We know that we'll only need to use a number once and then throw it away. So it's inefficient to use range() in this case


Using xrange
xrange() is the generator function version of range(). Instead of returning a list, it returns the next number in the expression when needed.

def get_sum(a,b): 
       return sum(xrange(a, b+1))

This is much more memory efficient. xrange() only returns the next number in the iterable as it's needed.

Generator functions
This is the power of using generator functions

imagine range() is implemented something like this:

def range(lower, upper):
     #this obviosuly needs to check inputs before blinding looping, but i'm ignoring that here
      r = list()
      next = lower
      while next < upper: #remember that range()'s upper is exclusive, not inclusive
           r.append(next)
           next += 1
      return r


whereas xrange is implemented like this
def xrange(lower, upper):
      next = lower
      while next < upper: #remember that range()'s upper is exclusive, not inclusive
           yield next
           next += 1


This is clearly alot more efficient than range()



Ref
2. C# version of generator function https://stackoverflow.com/a/38274782/1538717

Thursday, April 26, 2018

Python - how to make a mock return a different result the second time it's called

Problem
I am testing a function that has a loop that only stops when an element on a webpage is not found. So i need a mock to do different things on different iterations of the loop.

Solution
This is done by setting the side_effect as a list.

When find_element_by_css_selector is called, it will do the following:
loop iteration 1 - it'll pass back mockElement
loop iteration 2 - it'll throw an exception (this is what selenium does if the element is not found, so i'm just simulating that)


    @mock.patch("selenium.webdriver.chrome.webdriver.WebDriver")
    @mock.patch("selenium.webdriver.remote.webelement.WebElement")
    def test_whenButtonExists_weclickit(self, mockBrowser, mockElement):
        #arrange
        cmd = Cmd();
        cmd.browser = mockBrowser
        mockBrowser.find_element_by_css_selector.side_effect = [mockElement, Exception()]

        #act
        data = cmd.getDataFromWebsite()

        #assert
        mockElement.click.assert_called()

Sunday, April 22, 2018

Mocking in Python

How do I mock in Python?

Here's how:

the key is to import unittest.mock, and then add the @mock.patch decorator to the unit tests where you want to use a mock object. This causes it to pass a mock object in.

In the second test below, notice that i am causing the mock object to throw an exception.


from unittest import TestCase, mock
from Commander import Commander
from Command import Command

class CommanderTests(TestCase):

    @mock.patch("Command.Command")
    def test_whenrunAll_commandIsRan(self, mockCmd):
        #arrange
        commander = Commander();
        commander.addCommand(mockCmd);

        #act
        commander.runAll();

        #assert
        mockCmd.run.assert_called();

    @mock.patch("Command.Command")
    def test_whenrunAll_andCommandThrowsException_ExceptionDoesNotBubble(self, mockCmd):
        #arrange
        commander = Commander();
        mockCmd.run.side_effect = Exception();
        commander.addCommand(mockCmd);

        #act
        commander.runAll();

        #assert
        mockCmd.run.assert_called();

Saturday, March 24, 2018

Create shortcut to run python script from Notepad++

1. Run
2. Type cmd /C cd /d $(CURRENT_DIRECTORY) && python -i "$(FULL_CURRENT_PATH)" 
3. Click Save...
4. Choose a key combo, like Ctrl+F5
5. Click OK

Now you can press the shortcut to run your current script

Tuesday, March 20, 2018

Simulate key press event in Chrome

This is the correct way to send key events in chrome


Code:
var e = document.createEvent("Event")
e.initEvent("keydown",true,true)
e.keyCode = 38
e.which = 38

window.document.dispatchEvent(e)

Note: 38 = up arrow

Ref:

http://jsbin.com/awenaq/3

Searched for:
1. keyboardEvent code is empty
2. Chrome simulate key press

Tuesday, March 13, 2018

Paint in Raspberry Pi

Problem
I need to be able to create image files in Raspberry Pi (raspbian)

Solution
1. Install xpaint
  • sudo apt install xpaint

2. Open from the menu
  • 1. Pi button
  • 2. Graphics
  • 3. Xpaint