Skip to content

Instantly share code, notes, and snippets.

@aliaspooryorik
Last active July 20, 2017 07:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aliaspooryorik/b9c57f0705c15ac376a6ca125c8e4e8c to your computer and use it in GitHub Desktop.
Save aliaspooryorik/b9c57f0705c15ac376a6ca125c8e4e8c to your computer and use it in GitHub Desktop.
Uncle Bob :: Clean Code

Transformation Priority Premise

  • ({} → nil) no code at all → code that employs nil
  • (nil → constant)
  • (constant → constant+) a simple constant to a more complex constant
  • (constant → scalar) replacing a constant with a variable or an argument
  • (statement → statements) adding more unconditional statements.
  • (unconditional → if) splitting the execution path
  • (scalar → array)
  • (array → container)
  • (statement → tail-recursion)
  • (if → while)
  • (statement → non-tail-recursion)
  • (expression → function) replacing an expression with a function or algorithm
  • (variable → assignment) replacing the value of a variable.
  • (case) adding a case (or else) to an existing switch or if

Uncle Bob's Clean Code E23 0:42

Mock ontology

----<Abstract>----           ------------------
| Test Double    | <- IS-A - |      Fake      | - has logic, only use if you have to (e.g. across boundaries)
------------------           ------------------
        ^
        | IS-A
------------------
|     Dummy      |  - can't interact with it, just there to satisfy arguments
------------------
        ^
        | IS-A
------------------
|     Stub       | - returns fixed response
------------------
        ^
        | IS-A
------------------
|      Spy       | - returns fixed response, records calls (and can return the recording)
------------------
        ^
        | IS-A
------------------
|  'true' Mock   | - Same as a spy, except it also knows what should happen. The test asks the mock if the code ran as expected.
------------------

A dummy object returns nothing useful - it's just there to satisfy an argument value which is not actually used in the code you are testing.

for example:

function mytestforsomething() {
	Template = newDummyTemplate();
	assert(sut.generateReport(INVALID_DATE, Template)).isFalse();
}

function generateReport(invoiceDate, Template) {
	if (!isValidReportDate(invoiceDate)) {
		return false;
	}
	....
}

A stub is a dummy, but returns a fixed value, so we can drive the subject under test to run a specific bit of code.

For example:

function setup() {
	// this stub will return a UserID stub object
	authoriser = new RejectingAuthoriserStub();
	// this stub will return a User stub object
	userGateway = new UserGatewayStub();
	interactor.setAuthoriser(authoriser);
	interactor.setUserGateway(userGateway);
}

function mytestforfaileduserlogin() {
	var LoginRequest = new LoginRequest();
	LoginRequest.username = "invalid_username";
	LoginRequest.username = "invalid_password";

	// note that we are testing the `LOGIN_FAILURE_MESSAGE` path
	sut.login(LoginRequest);
	assert(sut.login(LoginRequest)).isFalse();
}


// LOGIN_INTERACTOR method...
void function login(LoginRequest) {
	var Response = new LoginResponse();
	var UserID = Authoriser.authorise(LoginRequest.username, LoginRequest.password);
	if (!UserID.isValid()) { // this is calling the stub
		Response.message = LOGIN_FAILURE_MESSAGE;
	} else {
		Response.message = LOGIN_SUCCESS_MESSAGE;
		
		var User = userGateway.getUser(UserID);
		response.name = user.getName();
		response.lastLoginTime = user.getLastLoginTime();
		response.loginCount = user.getLoginCount();
	}
	// note does not return anything
	presenter.presentResponse(response);
}

A spy, is a stub, functions do nothing, but return fixed values, but it remembers how it is called and reports that back. You would use a spy to assert that a function was called once, or was called with expected arguments.

A 'true' mock, spies on things but does the asserting check itself. It's a spy that knows what it should check. So the test asks the 'true' mock if everything was as expected.

A fake has behaviour, they can get complicated quickly and hard to maintain so you need to test the fakes! They are useful in integration tests, but most of the time avoid them if you can. So could do:

// A fake 
function isvalid(username, password) {
	if (username.startsWith("valid")) {
		return true;
	}
	return false;
} 


// tests
function mytestforgooduserlogin() {
	var LoginRequest = new LoginRequest();
	LoginRequest.username = "valid_username";
	LoginRequest.username = "valid_password";

	sut.login(LoginRequest);

	LoginResponse = presenterSpy.getInvokedResponse();
	assert(LoginResponse.name).is(UserStub.STUB_NAME);
	assert(LoginResponse.loginTime).is(UserStub.STUB_TIME);
	assert(LoginResponse.loginCount).is(UserStub.STUB_LOGIN_COUNT);
}

function mytestforfaileduserlogin() {
	var LoginRequest = new LoginRequest();
	LoginRequest.username = "invalid_username";
	LoginRequest.username = "invalid_password";

	sut.login(LoginRequest);

	LoginResponse = presenterSpy.getInvokedResponse();
	assert(LoginResponse.message).is(LoginInteractor.LOGIN_FAILURE_MESSAGE);
}

E23 Pt2 : 44:50

Test patterns:

  • Test specific subclasses -- override methods in the sut

  • Self-shunt pattern -- pass the actual test into the sut as the collaborator, so methods in the test are called (as stubbed methods), so the test becomes a spy. Normally you'd get the test to impliment one or more interfaces.

  • Humble object -- make calls to an external object go through a method in the sut so that you can override it. Use when you go across boundaries.


Not from clean code

Generative Testing

Used where you don't want to test implimentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment